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