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