1d89e855d96ec7f4295948c9045e8f5c63af72b2
[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 extends TypeAwareCodec<?, ?, ?>> {
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
102     protected abstract T booleanCodec(BooleanTypeDefinition type);
103
104     protected abstract T bitsCodec(BitsTypeDefinition type);
105
106     protected abstract T emptyCodec(EmptyTypeDefinition type);
107
108     protected abstract T enumCodec(EnumTypeDefinition type);
109
110     protected abstract T identityRefCodec(IdentityrefTypeDefinition type, QNameModule module);
111
112     protected abstract T instanceIdentifierCodec(InstanceIdentifierTypeDefinition type);
113
114     protected abstract T intCodec(IntegerTypeDefinition type);
115
116     protected abstract T decimalCodec(DecimalTypeDefinition type);
117
118     protected abstract T stringCodec(StringTypeDefinition type);
119
120     protected abstract T uintCodec(UnsignedIntegerTypeDefinition type);
121
122     protected abstract T unionCodec(UnionTypeDefinition type, List<T> codecs);
123
124     protected abstract T unknownCodec(UnknownTypeDefinition type);
125
126     private T getSimpleCodecFor(final TypeDefinition<?> type) {
127         // These types are expected to be fully-shared
128         if (type instanceof EmptyTypeDefinition) {
129             return emptyCodec((EmptyTypeDefinition) type);
130         } else if (type instanceof UnknownTypeDefinition) {
131             return unknownCodec((UnknownTypeDefinition) type);
132         }
133
134         // Now deal with simple types. Note we consider union composed of purely simple types a simple type itself.
135         // The checks here are optimized for common types.
136         final T ret;
137         if (type instanceof StringTypeDefinition) {
138             ret = stringCodec((StringTypeDefinition) type);
139         } else if (type instanceof IntegerTypeDefinition) {
140             ret = intCodec((IntegerTypeDefinition) type);
141         } else if (type instanceof UnsignedIntegerTypeDefinition) {
142             ret = uintCodec((UnsignedIntegerTypeDefinition) type);
143         } else if (type instanceof BooleanTypeDefinition) {
144             ret = booleanCodec((BooleanTypeDefinition) type);
145         } else if (type instanceof DecimalTypeDefinition) {
146             ret = decimalCodec((DecimalTypeDefinition) type);
147         } else if (type instanceof EnumTypeDefinition) {
148             ret = enumCodec((EnumTypeDefinition) type);
149         } else if (type instanceof BitsTypeDefinition) {
150             ret = bitsCodec((BitsTypeDefinition) type);
151         } else if (type instanceof UnionTypeDefinition) {
152             final UnionTypeDefinition union = (UnionTypeDefinition) type;
153             if (!isSimpleUnion(union)) {
154                 return null;
155             }
156             ret = createSimpleUnion(union);
157         } else if (type instanceof BinaryTypeDefinition) {
158             ret = binaryCodec((BinaryTypeDefinition) type);
159         } else if (type instanceof InstanceIdentifierTypeDefinition) {
160             return instanceIdentifierCodec((InstanceIdentifierTypeDefinition) type);
161         } else {
162             return null;
163         }
164
165         return cache.getSimple(type, Verify.verifyNotNull(ret));
166     }
167
168     private static boolean isSimpleUnion(final UnionTypeDefinition union) {
169         for (TypeDefinition<?> t : union.getTypes()) {
170             if (t instanceof IdentityrefTypeDefinition || t instanceof LeafrefTypeDefinition
171                     || (t instanceof UnionTypeDefinition && !isSimpleUnion((UnionTypeDefinition) t))) {
172                 LOG.debug("Type {} has non-simple subtype", t);
173                 return false;
174             }
175         }
176
177         LOG.debug("Type {} is simple", union);
178         return true;
179     }
180
181     private T createComplexCodecFor(final TypedSchemaNode schema, final TypeDefinition<?> type) {
182         if (type instanceof UnionTypeDefinition) {
183             return createComplexUnion(schema, (UnionTypeDefinition) type);
184         } else if (type instanceof LeafrefTypeDefinition) {
185             final TypeDefinition<?> target = SchemaContextUtil.getBaseTypeForLeafRef((LeafrefTypeDefinition) type,
186                 schemaContext, schema);
187             Verify.verifyNotNull(target, "Unable to find base type for leafref node %s type %s.", schema.getPath(),
188                     target);
189
190             final T ret = getSimpleCodecFor(target);
191             return ret != null ? ret : createComplexCodecFor(schema, target);
192         } else if (type instanceof IdentityrefTypeDefinition) {
193             return identityRefCodec((IdentityrefTypeDefinition) type, schema.getQName().getModule());
194         } else {
195             throw new IllegalArgumentException("Unsupported type " + type);
196         }
197     }
198
199     private T createSimpleUnion(final UnionTypeDefinition union) {
200         final List<TypeDefinition<?>> types = union.getTypes();
201         final List<T> codecs = new ArrayList<>(types.size());
202
203         for (TypeDefinition<?> type : types) {
204             T codec = cache.lookupSimple(type);
205             if (codec == null) {
206                 codec = Verify.verifyNotNull(getSimpleCodecFor(type), "Type %s did not resolve to a simple codec",
207                     type);
208             }
209
210             codecs.add(codec);
211         }
212
213         return unionCodec(union, codecs);
214     }
215
216     private T createComplexUnion(final TypedSchemaNode schema, final UnionTypeDefinition union) {
217         final List<TypeDefinition<?>> types = union.getTypes();
218         final List<T> codecs = new ArrayList<>(types.size());
219
220         for (TypeDefinition<?> type : types) {
221             T codec = cache.lookupSimple(type);
222             if (codec == null) {
223                 codec = getSimpleCodecFor(type);
224                 if (codec == null) {
225                     codec = createComplexCodecFor(schema, type);
226                 }
227             }
228
229             codecs.add(Verify.verifyNotNull(codec, "Schema %s subtype %s has no codec", schema, type));
230         }
231
232         return unionCodec(union, codecs);
233     }
234 }