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