Use TypedSchemaNode
[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.SchemaContext;
23 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
24 import org.opendaylight.yangtools.yang.model.api.TypedSchemaNode;
25 import org.opendaylight.yangtools.yang.model.api.type.EmptyTypeDefinition;
26 import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
27 import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
28 import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
29 import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
30 import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
33
34 /**
35  * Factory for creating JSON equivalents of codecs. Each instance of this object is bound to
36  * a particular {@link SchemaContext}, but can be reused by multiple {@link JSONNormalizedNodeStreamWriter}s.
37  */
38 @Beta
39 public final class JSONCodecFactory {
40     private static final Logger LOG = LoggerFactory.getLogger(JSONCodecFactory.class);
41     private static final JSONCodec<Object> NULL_CODEC = new JSONCodec<Object>() {
42         @Override
43         public Object deserialize(final String input) {
44             return null;
45         }
46
47         @Override
48         public String serialize(final Object input) {
49             return null;
50         }
51
52         @Override
53         public void serializeToWriter(final JsonWriter writer, final Object value) throws IOException {
54             // NOOP since codec is unkwown.
55             LOG.warn("Call of the serializeToWriter method on JSONCodecFactory.NULL_CODEC object. No operation performed.");
56         }
57     };
58
59     private final LoadingCache<TypedSchemaNode, JSONCodec<?>> codecs = CacheBuilder.newBuilder().softValues()
60             .build(new CacheLoader<TypedSchemaNode, JSONCodec<?>>() {
61         @Override
62         public JSONCodec<?> load(@Nonnull final TypedSchemaNode key) throws Exception {
63             final TypeDefinition<?> type = key.getType();
64             return createCodec(key, type);
65         }
66     });
67
68     private final SchemaContext schemaContext;
69     private final JSONCodec<?> iidCodec;
70
71     private JSONCodecFactory(final SchemaContext context) {
72         this.schemaContext = Preconditions.checkNotNull(context);
73         iidCodec = new JSONStringInstanceIdentifierCodec(context, this);
74     }
75
76     /**
77      * Instantiate a new codec factory attached to a particular context.
78      *
79      * @param context SchemaContext to which the factory should be bound
80      * @return A codec factory instance.
81      */
82     public static JSONCodecFactory create(final SchemaContext context) {
83         return new JSONCodecFactory(context);
84     }
85
86     private JSONCodec<?> createCodec(final DataSchemaNode key, final TypeDefinition<?> type) {
87         if (type instanceof LeafrefTypeDefinition) {
88             return createReferencedTypeCodec(key, (LeafrefTypeDefinition) type);
89         } else if (type instanceof IdentityrefTypeDefinition) {
90             return createIdentityrefTypeCodec(key);
91         } else if (type instanceof UnionTypeDefinition) {
92             return createUnionTypeCodec(key, (UnionTypeDefinition) type);
93         }
94         return createFromSimpleType(key, type);
95     }
96
97     private JSONCodec<?> createReferencedTypeCodec(final DataSchemaNode schema,
98             final LeafrefTypeDefinition type) {
99         // FIXME: Verify if this does indeed support leafref of leafref
100         final TypeDefinition<?> referencedType =
101                 SchemaContextUtil.getBaseTypeForLeafRef(type, getSchemaContext(), schema);
102         Verify.verifyNotNull(referencedType, "Unable to find base type for leafref node '%s'.", schema.getPath());
103         return createCodec(schema, referencedType);
104     }
105
106     private JSONCodec<QName> createIdentityrefTypeCodec(final DataSchemaNode schema) {
107         final JSONCodec<QName> jsonStringIdentityrefCodec =
108                 new JSONStringIdentityrefCodec(schemaContext, schema.getQName().getModule());
109         return jsonStringIdentityrefCodec;
110     }
111
112     private JSONCodec<Object> createUnionTypeCodec(final DataSchemaNode schema, final UnionTypeDefinition type) {
113         final JSONCodec<Object> jsonStringUnionCodec = new JSONStringUnionCodec(schema, type, this);
114         return jsonStringUnionCodec;
115     }
116
117     private JSONCodec<?> createFromSimpleType(final DataSchemaNode schema, final TypeDefinition<?> type) {
118         if (type instanceof InstanceIdentifierTypeDefinition) {
119             return iidCodec;
120         }
121         if (type instanceof EmptyTypeDefinition) {
122             return JSONEmptyCodec.INSTANCE;
123         }
124
125         final TypeDefinitionAwareCodec<Object, ?> codec = TypeDefinitionAwareCodec.from(type);
126         if (codec == null) {
127             LOG.debug("Codec for type \"{}\" is not implemented yet.", type.getQName()
128                     .getLocalName());
129             return NULL_CODEC;
130         }
131         return AbstractJSONCodec.create(codec);
132     }
133
134     SchemaContext getSchemaContext() {
135         return schemaContext;
136     }
137
138     JSONCodec<?> codecFor(final DataSchemaNode schema) {
139         Preconditions.checkArgument(schema instanceof TypedSchemaNode, "Unsupported node type %s", schema.getClass());
140         return codecs.getUnchecked((TypedSchemaNode) schema);
141     }
142
143     JSONCodec<?> codecFor(final DataSchemaNode schema, final TypeDefinition<?> unionSubType) {
144         return createCodec(schema, unionSubType);
145     }
146 }