2 * Copyright (c) 2017 Pantheon Technologies, s.r.o. and others. All rights reserved.
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
8 package org.opendaylight.yangtools.yang.data.util.codec;
10 import static com.google.common.base.Verify.verifyNotNull;
11 import static java.util.Objects.requireNonNull;
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;
47 * A type-to-codec factory base class with logic to efficiently lookup and cache codec instances,
48 * also dealing with union type composition.
50 * @author Robert Varga
52 * @param <T> Codec type
55 public abstract class AbstractCodecFactory<T extends TypeAwareCodec<?, ?, ?>> implements SchemaContextProvider {
56 private static final Logger LOG = LoggerFactory.getLogger(AbstractCodecFactory.class);
58 private final @NonNull CodecCache<T> cache;
60 private final @NonNull SchemaContext schemaContext;
62 protected AbstractCodecFactory(final SchemaContext schemaContext, final CodecCache<T> cache) {
63 this.schemaContext = requireNonNull(schemaContext);
64 this.cache = requireNonNull(cache);
68 public final SchemaContext getSchemaContext() {
72 public final @NonNull T codecFor(final TypedDataSchemaNode schema) {
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.
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
83 * We assume prevalence is in above order and that caching is effective.
85 final TypeDefinition<?> type = schema.getType();
86 T ret = cache.lookupSimple(type);
88 LOG.trace("Type {} hit simple {}", type, ret);
91 ret = cache.lookupComplex(schema);
93 LOG.trace("Type {} hit complex {}", type, ret);
97 // Dealing with simple types first...
98 ret = getSimpleCodecFor(type);
100 LOG.trace("Type {} miss simple {}", type, ret);
104 // ... and complex types afterwards
105 ret = createComplexCodecFor(schema, type);
106 LOG.trace("Type {} miss complex {}", type, ret);
107 return cache.getComplex(schema, ret);
110 protected abstract T binaryCodec(BinaryTypeDefinition type);
112 protected abstract T booleanCodec(BooleanTypeDefinition type);
114 protected abstract T bitsCodec(BitsTypeDefinition type);
116 protected abstract T emptyCodec(EmptyTypeDefinition type);
118 protected abstract T enumCodec(EnumTypeDefinition type);
120 protected abstract T identityRefCodec(IdentityrefTypeDefinition type, QNameModule module);
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);
126 protected abstract T int8Codec(Int8TypeDefinition type);
128 protected abstract T int16Codec(Int16TypeDefinition type);
130 protected abstract T int32Codec(Int32TypeDefinition type);
132 protected abstract T int64Codec(Int64TypeDefinition type);
134 protected abstract T decimalCodec(DecimalTypeDefinition type);
136 protected abstract T stringCodec(StringTypeDefinition type);
138 protected abstract T uint8Codec(Uint8TypeDefinition type);
140 protected abstract T uint16Codec(Uint16TypeDefinition type);
142 protected abstract T uint32Codec(Uint32TypeDefinition type);
144 protected abstract T uint64Codec(Uint64TypeDefinition type);
146 protected abstract T unionCodec(UnionTypeDefinition type, List<T> codecs);
148 protected abstract T unknownCodec(UnknownTypeDefinition type);
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);
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.
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)) {
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);
201 return cache.getSimple(type, verifyNotNull(ret));
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);
213 LOG.debug("Type {} is simple", union);
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(),
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());
231 throw new IllegalArgumentException("Unsupported type " + type);
235 private T createSimpleUnion(final UnionTypeDefinition union) {
236 final List<TypeDefinition<?>> types = union.getTypes();
237 final List<T> codecs = new ArrayList<>(types.size());
239 for (TypeDefinition<?> type : types) {
240 T codec = cache.lookupSimple(type);
242 codec = verifyNotNull(getSimpleCodecFor(type), "Type %s did not resolve to a simple codec", type);
248 return unionCodec(union, codecs);
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());
255 for (TypeDefinition<?> type : types) {
256 T codec = cache.lookupSimple(type);
258 codec = getSimpleCodecFor(type);
260 codec = createComplexCodecFor(schema, type);
264 codecs.add(verifyNotNull(codec, "Schema %s subtype %s has no codec", schema, type));
267 return unionCodec(union, codecs);