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