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