Merge "Introduced skeletons of Contributor and User guide."
[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.cache.CacheBuilder;
13 import com.google.common.cache.CacheLoader;
14 import com.google.common.cache.LoadingCache;
15 import com.google.gson.stream.JsonWriter;
16 import java.io.IOException;
17 import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec;
18 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
19 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
20 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
21 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
22 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
23 import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
24 import org.opendaylight.yangtools.yang.model.util.IdentityrefType;
25 import org.opendaylight.yangtools.yang.model.util.InstanceIdentifierType;
26 import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
29
30 /**
31  * Factory for creating JSON equivalents of codecs. Each instance of this object is bound to
32  * a particular {@link SchemaContext}, but can be reused by multiple {@link JSONNormalizedNodeStreamWriter}s.
33  */
34 @Beta
35 public final class JSONCodecFactory {
36     private static final Logger LOG = LoggerFactory.getLogger(JSONCodecFactory.class);
37     private static final JSONCodec<Object> NULL_CODEC = new JSONCodec<Object>() {
38         @Override
39         public Object deserialize(final String input) {
40             return null;
41         }
42
43         @Override
44         public String serialize(final Object input) {
45             return null;
46         }
47
48         @Override
49         public boolean needQuotes() {
50             return false;
51         }
52
53         @Override
54         public void serializeToWriter(JsonWriter writer, 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<Object>> codecs =
61             CacheBuilder.newBuilder().softValues().build(new CacheLoader<DataSchemaNode, JSONCodec<Object>>() {
62         @Override
63         public JSONCodec<Object> load(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     private final JSONCodec<?> idrefCodec;
79
80     private JSONCodecFactory(final SchemaContext context) {
81         this.schemaContext = Preconditions.checkNotNull(context);
82         iidCodec = new JSONStringInstanceIdentifierCodec(context);
83         idrefCodec = new JSONStringIdentityrefCodec(context);
84     }
85
86     /**
87      * Instantiate a new codec factory attached to a particular context.
88      *
89      * @param context SchemaContext to which the factory should be bound
90      * @return A codec factory instance.
91      */
92     public static JSONCodecFactory create(final SchemaContext context) {
93         return new JSONCodecFactory(context);
94     }
95
96     private static TypeDefinition<?> resolveBaseTypeFrom(final TypeDefinition<?> type) {
97         TypeDefinition<?> superType = type;
98         while (superType.getBaseType() != null) {
99             superType = superType.getBaseType();
100         }
101         return superType;
102     }
103
104     private JSONCodec<Object> createCodec(DataSchemaNode key, TypeDefinition<?> type) {
105         TypeDefinition<?> baseType = resolveBaseTypeFrom(type);
106         if (baseType instanceof LeafrefTypeDefinition) {
107             return createReferencedTypeCodec(key, (LeafrefTypeDefinition) baseType);
108         }
109         return createFromSimpleType(type);
110     }
111
112     private JSONCodec<Object> createReferencedTypeCodec(DataSchemaNode schema,
113             LeafrefTypeDefinition type) {
114         // FIXME: Verify if this does indeed support leafref of leafref
115         TypeDefinition<?> referencedType =
116                 SchemaContextUtil.getBaseTypeForLeafRef(type, getSchemaContext(), schema);
117         return createFromSimpleType(referencedType);
118     }
119
120     @SuppressWarnings("unchecked")
121     private JSONCodec<Object> createFromSimpleType(TypeDefinition<?> type) {
122         final TypeDefinition<?> baseType = resolveBaseTypeFrom(type);
123         if (baseType instanceof InstanceIdentifierType) {
124             return (JSONCodec<Object>) iidCodec;
125         }
126         if (baseType instanceof IdentityrefType) {
127             return (JSONCodec<Object>) idrefCodec;
128         }
129
130         final TypeDefinitionAwareCodec<Object, ?> codec = TypeDefinitionAwareCodec.from(type);
131         if (codec == null) {
132             LOG.debug("Codec for type \"{}\" is not implemented yet.", type.getQName()
133                     .getLocalName());
134             return NULL_CODEC;
135         }
136         return (JSONCodec<Object>) AbstractJSONCodec.create(codec);
137     }
138
139     SchemaContext getSchemaContext() {
140         return schemaContext;
141     }
142
143     JSONCodec<Object> codecFor(DataSchemaNode schema) {
144         return codecs.getUnchecked(schema);
145     }
146
147 }