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