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