6699581dcceec740205f4c327f56012c83c906d1
[yangtools.git] / yang / yang-data-util / src / main / java / org / opendaylight / yangtools / yang / data / util / codec / AbstractCodecFactory.java
1 /*
2  * Copyright (c) 2017 Pantheon Technologies, s.r.o. 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.util.codec;
9
10 import static com.google.common.base.Verify.verifyNotNull;
11 import static java.util.Objects.requireNonNull;
12
13 import java.util.ArrayList;
14 import java.util.List;
15 import javax.annotation.concurrent.ThreadSafe;
16 import org.opendaylight.yangtools.yang.common.QNameModule;
17 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
18 import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
19 import org.opendaylight.yangtools.yang.model.api.TypedDataSchemaNode;
20 import org.opendaylight.yangtools.yang.model.api.type.BinaryTypeDefinition;
21 import org.opendaylight.yangtools.yang.model.api.type.BitsTypeDefinition;
22 import org.opendaylight.yangtools.yang.model.api.type.BooleanTypeDefinition;
23 import org.opendaylight.yangtools.yang.model.api.type.DecimalTypeDefinition;
24 import org.opendaylight.yangtools.yang.model.api.type.EmptyTypeDefinition;
25 import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
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.Int16TypeDefinition;
29 import org.opendaylight.yangtools.yang.model.api.type.Int32TypeDefinition;
30 import org.opendaylight.yangtools.yang.model.api.type.Int64TypeDefinition;
31 import org.opendaylight.yangtools.yang.model.api.type.Int8TypeDefinition;
32 import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
33 import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
34 import org.opendaylight.yangtools.yang.model.api.type.Uint16TypeDefinition;
35 import org.opendaylight.yangtools.yang.model.api.type.Uint32TypeDefinition;
36 import org.opendaylight.yangtools.yang.model.api.type.Uint64TypeDefinition;
37 import org.opendaylight.yangtools.yang.model.api.type.Uint8TypeDefinition;
38 import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
39 import org.opendaylight.yangtools.yang.model.api.type.UnknownTypeDefinition;
40 import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
41 import org.slf4j.Logger;
42 import org.slf4j.LoggerFactory;
43
44 /**
45  * A type-to-codec factory base class with logic to efficiently lookup and cache codec instances,
46  * also dealing with union type composition.
47  *
48  * @author Robert Varga
49  *
50  * @param <T> Codec type
51  */
52 @ThreadSafe
53 public abstract class AbstractCodecFactory<T extends TypeAwareCodec<?, ?, ?>> {
54     private static final Logger LOG = LoggerFactory.getLogger(AbstractCodecFactory.class);
55
56     private final CodecCache<T> cache;
57
58     private final SchemaContext schemaContext;
59
60     protected AbstractCodecFactory(final SchemaContext schemaContext, final CodecCache<T> cache) {
61         this.schemaContext = requireNonNull(schemaContext);
62         this.cache = requireNonNull(cache);
63     }
64
65     public final SchemaContext getSchemaContext() {
66         return schemaContext;
67     }
68
69     public final T codecFor(final TypedDataSchemaNode schema) {
70         /*
71          * There are many trade-offs to be made here. We need the common case being as fast as possible while reusing
72          * codecs as much as possible.
73          *
74          * This gives us essentially four classes of codecs:
75          * - simple codecs, which are based on the type definition only
76          * - complex codecs, which depend on both type definition and the leaf
77          * - null codec, which does not depend on anything
78          * - instance identifier codec, which is based on namespace mapping
79          *
80          * We assume prevalence is in above order and that caching is effective. We therefore
81          */
82         final TypeDefinition<?> type = schema.getType();
83         T ret = cache.lookupSimple(type);
84         if (ret != null) {
85             LOG.trace("Type {} hit simple {}", type, ret);
86             return ret;
87         }
88         ret = cache.lookupComplex(schema);
89         if (ret != null) {
90             LOG.trace("Type {} hit complex {}", type, ret);
91             return ret;
92         }
93
94         // Dealing with simple types first...
95         ret = getSimpleCodecFor(type);
96         if (ret != null) {
97             LOG.trace("Type {} miss simple {}", type, ret);
98             return ret;
99         }
100
101         // ... and complex types afterwards
102         ret = createComplexCodecFor(schema, type);
103         LOG.trace("Type {} miss complex {}", type, ret);
104         return cache.getComplex(schema, ret);
105     }
106
107     protected abstract T binaryCodec(BinaryTypeDefinition type);
108
109     protected abstract T booleanCodec(BooleanTypeDefinition type);
110
111     protected abstract T bitsCodec(BitsTypeDefinition type);
112
113     protected abstract T emptyCodec(EmptyTypeDefinition type);
114
115     protected abstract T enumCodec(EnumTypeDefinition type);
116
117     protected abstract T identityRefCodec(IdentityrefTypeDefinition type, QNameModule module);
118
119     protected abstract T instanceIdentifierCodec(InstanceIdentifierTypeDefinition type);
120
121     protected abstract T int8Codec(Int8TypeDefinition type);
122
123     protected abstract T int16Codec(Int16TypeDefinition type);
124
125     protected abstract T int32Codec(Int32TypeDefinition type);
126
127     protected abstract T int64Codec(Int64TypeDefinition type);
128
129     protected abstract T decimalCodec(DecimalTypeDefinition type);
130
131     protected abstract T stringCodec(StringTypeDefinition type);
132
133     protected abstract T uint8Codec(Uint8TypeDefinition type);
134
135     protected abstract T uint16Codec(Uint16TypeDefinition type);
136
137     protected abstract T uint32Codec(Uint32TypeDefinition type);
138
139     protected abstract T uint64Codec(Uint64TypeDefinition type);
140
141     protected abstract T unionCodec(UnionTypeDefinition type, List<T> codecs);
142
143     protected abstract T unknownCodec(UnknownTypeDefinition type);
144
145     private T getSimpleCodecFor(final TypeDefinition<?> type) {
146         // These types are expected to be fully-shared
147         if (type instanceof EmptyTypeDefinition) {
148             return emptyCodec((EmptyTypeDefinition) type);
149         } else if (type instanceof UnknownTypeDefinition) {
150             return unknownCodec((UnknownTypeDefinition) type);
151         }
152
153         // Now deal with simple types. Note we consider union composed of purely simple types a simple type itself.
154         // The checks here are optimized for common types.
155         final T ret;
156         if (type instanceof StringTypeDefinition) {
157             ret = stringCodec((StringTypeDefinition) type);
158         } else if (type instanceof Int8TypeDefinition) {
159             ret = int8Codec((Int8TypeDefinition) type);
160         } else if (type instanceof Int16TypeDefinition) {
161             ret = int16Codec((Int16TypeDefinition) type);
162         } else if (type instanceof Int32TypeDefinition) {
163             ret = int32Codec((Int32TypeDefinition) type);
164         } else if (type instanceof Int64TypeDefinition) {
165             ret = int64Codec((Int64TypeDefinition) type);
166         } else if (type instanceof Uint8TypeDefinition) {
167             ret = uint8Codec((Uint8TypeDefinition) type);
168         } else if (type instanceof Uint16TypeDefinition) {
169             ret = uint16Codec((Uint16TypeDefinition) type);
170         } else if (type instanceof Uint32TypeDefinition) {
171             ret = uint32Codec((Uint32TypeDefinition) type);
172         } else if (type instanceof Uint64TypeDefinition) {
173             ret = uint64Codec((Uint64TypeDefinition) type);
174         } else if (type instanceof BooleanTypeDefinition) {
175             ret = booleanCodec((BooleanTypeDefinition) type);
176         } else if (type instanceof DecimalTypeDefinition) {
177             ret = decimalCodec((DecimalTypeDefinition) type);
178         } else if (type instanceof EnumTypeDefinition) {
179             ret = enumCodec((EnumTypeDefinition) type);
180         } else if (type instanceof BitsTypeDefinition) {
181             ret = bitsCodec((BitsTypeDefinition) type);
182         } else if (type instanceof UnionTypeDefinition) {
183             final UnionTypeDefinition union = (UnionTypeDefinition) type;
184             if (!isSimpleUnion(union)) {
185                 return null;
186             }
187             ret = createSimpleUnion(union);
188         } else if (type instanceof BinaryTypeDefinition) {
189             ret = binaryCodec((BinaryTypeDefinition) type);
190         } else if (type instanceof InstanceIdentifierTypeDefinition) {
191             return instanceIdentifierCodec((InstanceIdentifierTypeDefinition) type);
192         } else {
193             return null;
194         }
195
196         return cache.getSimple(type, verifyNotNull(ret));
197     }
198
199     private static boolean isSimpleUnion(final UnionTypeDefinition union) {
200         for (TypeDefinition<?> t : union.getTypes()) {
201             if (t instanceof IdentityrefTypeDefinition || t instanceof LeafrefTypeDefinition
202                     || t instanceof UnionTypeDefinition && !isSimpleUnion((UnionTypeDefinition) t)) {
203                 LOG.debug("Type {} has non-simple subtype", t);
204                 return false;
205             }
206         }
207
208         LOG.debug("Type {} is simple", union);
209         return true;
210     }
211
212     private T createComplexCodecFor(final TypedDataSchemaNode schema, final TypeDefinition<?> type) {
213         if (type instanceof UnionTypeDefinition) {
214             return createComplexUnion(schema, (UnionTypeDefinition) type);
215         } else if (type instanceof LeafrefTypeDefinition) {
216             final TypeDefinition<?> target = SchemaContextUtil.getBaseTypeForLeafRef((LeafrefTypeDefinition) type,
217                 schemaContext, schema);
218             verifyNotNull(target, "Unable to find base type for leafref node %s type %s.", schema.getPath(),
219                     target);
220
221             final T ret = getSimpleCodecFor(target);
222             return ret != null ? ret : createComplexCodecFor(schema, target);
223         } else if (type instanceof IdentityrefTypeDefinition) {
224             return identityRefCodec((IdentityrefTypeDefinition) type, schema.getQName().getModule());
225         } else {
226             throw new IllegalArgumentException("Unsupported type " + type);
227         }
228     }
229
230     private T createSimpleUnion(final UnionTypeDefinition union) {
231         final List<TypeDefinition<?>> types = union.getTypes();
232         final List<T> codecs = new ArrayList<>(types.size());
233
234         for (TypeDefinition<?> type : types) {
235             T codec = cache.lookupSimple(type);
236             if (codec == null) {
237                 codec = verifyNotNull(getSimpleCodecFor(type), "Type %s did not resolve to a simple codec", type);
238             }
239
240             codecs.add(codec);
241         }
242
243         return unionCodec(union, codecs);
244     }
245
246     private T createComplexUnion(final TypedDataSchemaNode schema, final UnionTypeDefinition union) {
247         final List<TypeDefinition<?>> types = union.getTypes();
248         final List<T> codecs = new ArrayList<>(types.size());
249
250         for (TypeDefinition<?> type : types) {
251             T codec = cache.lookupSimple(type);
252             if (codec == null) {
253                 codec = getSimpleCodecFor(type);
254                 if (codec == null) {
255                     codec = createComplexCodecFor(schema, type);
256                 }
257             }
258
259             codecs.add(verifyNotNull(codec, "Schema %s subtype %s has no codec", schema, type));
260         }
261
262         return unionCodec(union, codecs);
263     }
264 }