53998d82d78bb10d60fb6715d805b4d9a2e23037
[yangtools.git] / yang / yang-data-codec-gson / src / main / java / org / opendaylight / yangtools / yang / data / codec / gson / JSONCodecFactory.java
1 /*
2  * Copyright (c) 2014 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.yangtools.yang.data.codec.gson;
9
10 import com.google.common.annotations.Beta;
11 import com.google.common.base.Preconditions;
12 import com.google.common.base.Verify;
13 import com.google.common.cache.CacheBuilder;
14 import com.google.common.cache.CacheLoader;
15 import com.google.common.cache.LoadingCache;
16 import com.google.gson.stream.JsonWriter;
17 import java.io.IOException;
18 import javax.annotation.Nonnull;
19 import org.opendaylight.yangtools.yang.common.QName;
20 import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec;
21 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
22 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
23 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
24 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
25 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
26 import org.opendaylight.yangtools.yang.model.api.type.EmptyTypeDefinition;
27 import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
28 import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
29 import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
30 import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
31 import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
32 import org.slf4j.Logger;
33 import org.slf4j.LoggerFactory;
34
35 /**
36  * Factory for creating JSON equivalents of codecs. Each instance of this object is bound to
37  * a particular {@link SchemaContext}, but can be reused by multiple {@link JSONNormalizedNodeStreamWriter}s.
38  */
39 @Beta
40 public final class JSONCodecFactory {
41     private static final Logger LOG = LoggerFactory.getLogger(JSONCodecFactory.class);
42     private static final JSONCodec<Object> NULL_CODEC = new JSONCodec<Object>() {
43         @Override
44         public Object deserialize(final String input) {
45             return null;
46         }
47
48         @Override
49         public String serialize(final Object input) {
50             return null;
51         }
52
53         @Override
54         public void serializeToWriter(final JsonWriter writer, final Object value) throws IOException {
55             // NOOP since codec is unkwown.
56             LOG.warn("Call of the serializeToWriter method on JSONCodecFactory.NULL_CODEC object. No operation performed.");
57         }
58     };
59
60     private final LoadingCache<DataSchemaNode, JSONCodec<?>> codecs =
61             CacheBuilder.newBuilder().softValues().build(new CacheLoader<DataSchemaNode, JSONCodec<?>>() {
62         @Override
63         public JSONCodec<?> load(@Nonnull final DataSchemaNode key) throws Exception {
64             final TypeDefinition<?> type;
65             if (key instanceof LeafSchemaNode) {
66                 type = ((LeafSchemaNode) key).getType();
67             } else if (key instanceof LeafListSchemaNode) {
68                 type = ((LeafListSchemaNode) key).getType();
69             } else {
70                 throw new IllegalArgumentException("Not supported node type " + key.getClass().getName());
71             }
72             return createCodec(key,type);
73         }
74     });
75
76     private final SchemaContext schemaContext;
77     private final JSONCodec<?> iidCodec;
78
79     private JSONCodecFactory(final SchemaContext context) {
80         this.schemaContext = Preconditions.checkNotNull(context);
81         iidCodec = new JSONStringInstanceIdentifierCodec(context, this);
82     }
83
84     /**
85      * Instantiate a new codec factory attached to a particular context.
86      *
87      * @param context SchemaContext to which the factory should be bound
88      * @return A codec factory instance.
89      */
90     public static JSONCodecFactory create(final SchemaContext context) {
91         return new JSONCodecFactory(context);
92     }
93
94     private JSONCodec<?> createCodec(final DataSchemaNode key, final TypeDefinition<?> type) {
95         if (type instanceof LeafrefTypeDefinition) {
96             return createReferencedTypeCodec(key, (LeafrefTypeDefinition) type);
97         } else if (type instanceof IdentityrefTypeDefinition) {
98             return createIdentityrefTypeCodec(key);
99         } else if (type instanceof UnionTypeDefinition) {
100             return createUnionTypeCodec(key, (UnionTypeDefinition) type);
101         }
102         return createFromSimpleType(key, type);
103     }
104
105     private JSONCodec<?> createReferencedTypeCodec(final DataSchemaNode schema,
106             final LeafrefTypeDefinition type) {
107         // FIXME: Verify if this does indeed support leafref of leafref
108         final TypeDefinition<?> referencedType =
109                 SchemaContextUtil.getBaseTypeForLeafRef(type, getSchemaContext(), schema);
110         Verify.verifyNotNull(referencedType, "Unable to find base type for leafref node '%s'.", schema.getPath());
111         return createCodec(schema, referencedType);
112     }
113
114     private JSONCodec<QName> createIdentityrefTypeCodec(final DataSchemaNode schema) {
115         final JSONCodec<QName> jsonStringIdentityrefCodec =
116                 new JSONStringIdentityrefCodec(schemaContext, schema.getQName().getModule());
117         return jsonStringIdentityrefCodec;
118     }
119
120     private JSONCodec<Object> createUnionTypeCodec(final DataSchemaNode schema, final UnionTypeDefinition type) {
121         final JSONCodec<Object> jsonStringUnionCodec = new JSONStringUnionCodec(schema, type, this);
122         return jsonStringUnionCodec;
123     }
124
125     private JSONCodec<?> createFromSimpleType(final DataSchemaNode schema, final TypeDefinition<?> type) {
126         if (type instanceof InstanceIdentifierTypeDefinition) {
127             return iidCodec;
128         }
129         if (type instanceof EmptyTypeDefinition) {
130             return JSONEmptyCodec.INSTANCE;
131         }
132
133         final TypeDefinitionAwareCodec<Object, ?> codec = TypeDefinitionAwareCodec.from(type);
134         if (codec == null) {
135             LOG.debug("Codec for type \"{}\" is not implemented yet.", type.getQName()
136                     .getLocalName());
137             return NULL_CODEC;
138         }
139         return AbstractJSONCodec.create(codec);
140     }
141
142     SchemaContext getSchemaContext() {
143         return schemaContext;
144     }
145
146     JSONCodec<?> codecFor(final DataSchemaNode schema) {
147         return codecs.getUnchecked(schema);
148     }
149
150     JSONCodec<?> codecFor(final DataSchemaNode schema, final TypeDefinition<?> unionSubType) {
151         return createCodec(schema, unionSubType);
152     }
153 }