2 * Copyright (c) 2014 Cisco Systems, Inc. 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.sal.binding.generator.impl;
10 import java.lang.ref.WeakReference;
11 import java.lang.reflect.Field;
12 import java.lang.reflect.ParameterizedType;
13 import java.util.ArrayList;
14 import java.util.Collection;
15 import java.util.Collections;
16 import java.util.HashMap;
17 import java.util.HashSet;
18 import java.util.Iterator;
19 import java.util.List;
21 import java.util.Map.Entry;
22 import java.util.Objects;
24 import java.util.WeakHashMap;
25 import java.util.concurrent.Callable;
26 import java.util.concurrent.ConcurrentHashMap;
27 import java.util.concurrent.ConcurrentMap;
29 import org.opendaylight.yangtools.binding.generator.util.ReferencedTypeImpl;
30 import org.opendaylight.yangtools.binding.generator.util.Types;
31 import org.opendaylight.yangtools.concepts.Delegator;
32 import org.opendaylight.yangtools.concepts.Identifiable;
33 import org.opendaylight.yangtools.sal.binding.generator.util.ClassLoaderUtils;
34 import org.opendaylight.yangtools.sal.binding.generator.util.CodeGenerationException;
35 import org.opendaylight.yangtools.sal.binding.model.api.ConcreteType;
36 import org.opendaylight.yangtools.sal.binding.model.api.Type;
37 import org.opendaylight.yangtools.sal.binding.model.api.type.builder.GeneratedTOBuilder;
38 import org.opendaylight.yangtools.sal.binding.model.api.type.builder.GeneratedTypeBuilder;
39 import org.opendaylight.yangtools.yang.binding.Augmentable;
40 import org.opendaylight.yangtools.yang.binding.Augmentation;
41 import org.opendaylight.yangtools.yang.binding.BaseIdentity;
42 import org.opendaylight.yangtools.yang.binding.BindingCodec;
43 import org.opendaylight.yangtools.yang.binding.DataContainer;
44 import org.opendaylight.yangtools.yang.binding.DataObject;
45 import org.opendaylight.yangtools.yang.binding.Identifier;
46 import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
47 import org.opendaylight.yangtools.yang.common.QName;
48 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
49 import org.opendaylight.yangtools.yang.data.api.Node;
50 import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl;
51 import org.opendaylight.yangtools.yang.data.impl.codec.AugmentationCodec;
52 import org.opendaylight.yangtools.yang.data.impl.codec.ChoiceCaseCodec;
53 import org.opendaylight.yangtools.yang.data.impl.codec.ChoiceCodec;
54 import org.opendaylight.yangtools.yang.data.impl.codec.CodecRegistry;
55 import org.opendaylight.yangtools.yang.data.impl.codec.DataContainerCodec;
56 import org.opendaylight.yangtools.yang.data.impl.codec.DomCodec;
57 import org.opendaylight.yangtools.yang.data.impl.codec.IdentifierCodec;
58 import org.opendaylight.yangtools.yang.data.impl.codec.IdentityCodec;
59 import org.opendaylight.yangtools.yang.data.impl.codec.InstanceIdentifierCodec;
60 import org.opendaylight.yangtools.yang.data.impl.codec.ValueWithQName;
61 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
62 import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
63 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
64 import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
65 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
66 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
67 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
68 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
69 import org.opendaylight.yangtools.yang.model.api.Module;
70 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
71 import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
72 import org.opendaylight.yangtools.yang.model.api.SchemaNode;
73 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
74 import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
75 import org.slf4j.Logger;
76 import org.slf4j.LoggerFactory;
78 import com.google.common.base.Preconditions;
80 public class LazyGeneratedCodecRegistry implements //
82 SchemaContextListener, //
85 private final static Logger LOG = LoggerFactory.getLogger(LazyGeneratedCodecRegistry.class);
86 private final static LateMixinCodec NOT_READY_CODEC = new LateMixinCodec();
88 private final InstanceIdentifierCodec instanceIdentifierCodec = new InstanceIdentifierCodecImpl(this);
89 private final IdentityCompositeCodec identityRefCodec = new IdentityCompositeCodec();
91 private TransformerGenerator generator;
93 // Concrete class to codecs
94 private static final Map<Class<?>, DataContainerCodec<?>> containerCodecs = Collections
95 .synchronizedMap(new WeakHashMap<Class<?>, DataContainerCodec<?>>());
96 private static final Map<Class<?>, IdentifierCodec<?>> identifierCodecs = Collections
97 .synchronizedMap(new WeakHashMap<Class<?>, IdentifierCodec<?>>());
98 private static final Map<Class<?>, ChoiceCodecImpl<?>> choiceCodecs = Collections
99 .synchronizedMap(new WeakHashMap<Class<?>, ChoiceCodecImpl<?>>());
100 private static final Map<Class<?>, ChoiceCaseCodecImpl<?>> caseCodecs = Collections
101 .synchronizedMap(new WeakHashMap<Class<?>, ChoiceCaseCodecImpl<?>>());
102 private static final Map<Class<?>, AugmentableCompositeCodec> augmentableCodecs = Collections
103 .synchronizedMap(new WeakHashMap<Class<?>, AugmentableCompositeCodec>());
104 private static final Map<Class<?>, AugmentationCodec<?>> augmentationCodecs = Collections
105 .synchronizedMap(new WeakHashMap<Class<?>, AugmentationCodec<?>>());
106 private static final Map<Class<?>, QName> identityQNames = Collections
107 .synchronizedMap(new WeakHashMap<Class<?>, QName>());
108 private static final Map<QName, Type> qnamesToIdentityMap = new ConcurrentHashMap<>();
109 /** Binding type to encountered classes mapping **/
110 @SuppressWarnings("rawtypes")
111 private static final Map<Type, WeakReference<Class>> typeToClass = new ConcurrentHashMap<>();
113 @SuppressWarnings("rawtypes")
114 private static final ConcurrentMap<Type, ChoiceCaseCodecImpl> typeToCaseCodecs = new ConcurrentHashMap<>();
116 private final CaseClassMapFacade classToCaseRawCodec = new CaseClassMapFacade();
118 private static final Map<SchemaPath, GeneratedTypeBuilder> pathToType = new ConcurrentHashMap<>();
119 private static final Map<List<QName>, Type> pathToInstantiatedType = new ConcurrentHashMap<>();
120 private static final Map<Type, QName> typeToQname = new ConcurrentHashMap<>();
121 private static final Map<AugmentationSchema, Type> augmentToType = new ConcurrentHashMap<>();
123 private final SchemaLock lock;
125 private SchemaContext currentSchema;
127 private final GeneratedClassLoadingStrategy classLoadingStrategy;
129 LazyGeneratedCodecRegistry(SchemaLock lock, GeneratedClassLoadingStrategy identityClassLoadingStrategy) {
130 this.lock = Preconditions.checkNotNull(lock);
131 this.classLoadingStrategy = identityClassLoadingStrategy;
134 public SchemaLock getLock() {
138 public TransformerGenerator getGenerator() {
142 public void setGenerator(TransformerGenerator generator) {
143 this.generator = generator;
147 public InstanceIdentifierCodec getInstanceIdentifierCodec() {
148 return instanceIdentifierCodec;
152 public <T extends Augmentation<?>> AugmentationCodec<T> getCodecForAugmentation(Class<T> object) {
153 AugmentationCodec<T> codec = null;
154 @SuppressWarnings("rawtypes")
155 AugmentationCodec potentialCodec = augmentationCodecs.get(object);
156 if (potentialCodec != null) {
157 codec = potentialCodec;
160 lock.waitForSchema(object);
161 Class<? extends BindingCodec<Map<QName, Object>, Object>> augmentRawCodec = generator
162 .augmentationTransformerFor(object);
163 BindingCodec<Map<QName, Object>, Object> rawCodec = augmentRawCodec.newInstance();
164 codec = new AugmentationCodecWrapper<T>(rawCodec);
165 augmentationCodecs.put(object, codec);
166 } catch (InstantiationException e) {
167 LOG.error("Can not instantiate raw augmentation codec {}", object.getSimpleName(), e);
168 } catch (IllegalAccessException e) {
170 "Run-time consistency issue: constructor {} is not available. This indicates either a code generation bug or a misconfiguration of JVM.",
171 object.getSimpleName(), e);
173 Class<? extends Augmentable<?>> objectSupertype = getAugmentableArgumentFrom(object);
174 if (objectSupertype != null) {
175 getAugmentableCodec(objectSupertype).addAugmentationCodec(object, codec);
177 LOG.warn("Could not find augmentation target for augmentation {}", object);
183 public QName getQNameForAugmentation(Class<?> cls) {
184 Preconditions.checkArgument(Augmentation.class.isAssignableFrom(cls));
185 return getCodecForAugmentation((Class<? extends Augmentation>) cls).getAugmentationQName();
188 private static Class<? extends Augmentable<?>> getAugmentableArgumentFrom(
189 final Class<? extends Augmentation<?>> augmentation) {
191 Class<? extends Augmentable<?>> ret = ClassLoaderUtils.withClassLoader(augmentation.getClassLoader(),
192 new Callable<Class<? extends Augmentable<?>>>() {
194 @SuppressWarnings("unchecked")
195 public Class<? extends Augmentable<?>> call() throws Exception {
196 for (java.lang.reflect.Type supertype : augmentation.getGenericInterfaces()) {
197 if (supertype instanceof ParameterizedType
198 && Augmentation.class.equals(((ParameterizedType) supertype).getRawType())) {
199 ParameterizedType augmentationGeneric = (ParameterizedType) supertype;
200 return (Class<? extends Augmentable<?>>) augmentationGeneric
201 .getActualTypeArguments()[0];
208 } catch (Exception e) {
209 LOG.debug("Could not find augmentable for {} using {}", augmentation, augmentation.getClassLoader(), e);
215 public Class<?> getClassForPath(List<QName> names) {
216 DataSchemaNode node = getSchemaNode(names);
217 SchemaPath path = node.getPath();
218 Type type = pathToType.get(path);
220 type = new ReferencedTypeImpl(type.getPackageName(), type.getName());
222 type = pathToInstantiatedType.get(names);
224 @SuppressWarnings("rawtypes")
225 WeakReference<Class> weakRef = typeToClass.get(type);
226 if (weakRef == null) {
227 LOG.error("Could not find loaded class for path: {} and type: {}", path, type.getFullyQualifiedName());
229 return weakRef.get();
233 public void putPathToClass(List<QName> names, Class<?> cls) {
234 Type reference = Types.typeForClass(cls);
235 pathToInstantiatedType.put(names, reference);
236 bindingClassEncountered(cls);
240 public IdentifierCodec<?> getKeyCodecForPath(List<QName> names) {
241 @SuppressWarnings("unchecked")
242 Class<? extends Identifiable<?>> cls = (Class<? extends Identifiable<?>>) getClassForPath(names);
243 return getIdentifierCodecForIdentifiable(cls);
247 public <T extends DataContainer> DataContainerCodec<T> getCodecForDataObject(Class<T> type) {
248 @SuppressWarnings("unchecked")
249 DataContainerCodec<T> ret = (DataContainerCodec<T>) containerCodecs.get(type);
253 Class<? extends BindingCodec<Map<QName, Object>, Object>> newType = generator.transformerFor(type);
254 BindingCodec<Map<QName, Object>, Object> rawCodec = newInstanceOf(newType);
255 DataContainerCodecImpl<T> newWrapper = new DataContainerCodecImpl<>(rawCodec);
256 containerCodecs.put(type, newWrapper);
261 @SuppressWarnings("rawtypes")
262 public void bindingClassEncountered(Class cls) {
264 ConcreteType typeRef = Types.typeForClass(cls);
265 if (typeToClass.containsKey(typeRef)) {
268 LOG.trace("Binding Class {} encountered.", cls);
269 WeakReference<Class> weakRef = new WeakReference<>(cls);
270 typeToClass.put(typeRef, weakRef);
271 if (Augmentation.class.isAssignableFrom(cls)) {
273 } else if (DataObject.class.isAssignableFrom(cls)) {
274 @SuppressWarnings({ "unchecked", "unused" })
275 Object cdc = getCodecForDataObject((Class<? extends DataObject>) cls);
280 public void onClassProcessed(Class<?> cls) {
281 ConcreteType typeRef = Types.typeForClass(cls);
282 if (typeToClass.containsKey(typeRef)) {
285 LOG.trace("Binding Class {} encountered.", cls);
286 @SuppressWarnings("rawtypes")
287 WeakReference<Class> weakRef = new WeakReference<Class>(cls);
288 typeToClass.put(typeRef, weakRef);
291 private DataSchemaNode getSchemaNode(List<QName> path) {
292 QName firstNode = path.get(0);
293 DataNodeContainer previous = currentSchema.findModuleByNamespaceAndRevision(firstNode.getNamespace(),
294 firstNode.getRevision());
295 Iterator<QName> iterator = path.iterator();
296 while (iterator.hasNext()) {
297 QName arg = iterator.next();
298 DataSchemaNode currentNode = previous.getDataChildByName(arg);
299 if (currentNode == null && previous instanceof DataNodeContainer) {
300 currentNode = searchInChoices(previous, arg);
302 if (currentNode instanceof DataNodeContainer) {
303 previous = (DataNodeContainer) currentNode;
304 } else if (currentNode instanceof LeafSchemaNode || currentNode instanceof LeafListSchemaNode) {
305 Preconditions.checkState(!iterator.hasNext(), "Path tries to nest inside leaf node.");
309 return (DataSchemaNode) previous;
312 private DataSchemaNode searchInChoices(DataNodeContainer node, QName arg) {
313 Set<DataSchemaNode> children = node.getChildNodes();
314 for (DataSchemaNode child : children) {
315 if (child instanceof ChoiceNode) {
316 ChoiceNode choiceNode = (ChoiceNode) child;
317 DataSchemaNode potential = searchInCases(choiceNode, arg);
318 if (potential != null) {
326 private DataSchemaNode searchInCases(ChoiceNode choiceNode, QName arg) {
327 Set<ChoiceCaseNode> cases = choiceNode.getCases();
328 for (ChoiceCaseNode caseNode : cases) {
329 DataSchemaNode node = caseNode.getDataChildByName(arg);
337 private <T> T newInstanceOf(Class<?> newType) {
339 @SuppressWarnings("unchecked")
340 T ret = (T) newType.newInstance();
342 } catch (InstantiationException e) {
343 throw new IllegalStateException(e);
344 } catch (IllegalAccessException e) {
345 throw new IllegalStateException(e);
350 public <T extends Identifiable<?>> IdentifierCodec<?> getIdentifierCodecForIdentifiable(Class<T> type) {
351 IdentifierCodec<?> obj = identifierCodecs.get(type);
355 Class<? extends BindingCodec<Map<QName, Object>, Object>> newCodec = generator
356 .keyTransformerForIdentifiable(type);
357 BindingCodec<Map<QName, Object>, Object> newInstance;
358 newInstance = newInstanceOf(newCodec);
359 IdentifierCodecImpl<?> newWrapper = new IdentifierCodecImpl<>(newInstance);
360 identifierCodecs.put(type, newWrapper);
365 public IdentityCodec<?> getIdentityCodec() {
366 return identityRefCodec;
370 public <T extends BaseIdentity> IdentityCodec<T> getCodecForIdentity(Class<T> codec) {
371 bindingClassEncountered(codec);
372 return identityRefCodec;
376 public void onCodecCreated(Class<?> cls) {
377 CodecMapping.setIdentifierCodec(cls, instanceIdentifierCodec);
378 CodecMapping.setIdentityRefCodec(cls, identityRefCodec);
382 public <T extends Identifier<?>> IdentifierCodec<T> getCodecForIdentifier(Class<T> object) {
383 @SuppressWarnings("unchecked")
384 IdentifierCodec<T> obj = (IdentifierCodec<T>) identifierCodecs.get(object);
388 Class<? extends BindingCodec<Map<QName, Object>, Object>> newCodec = generator
389 .keyTransformerForIdentifier(object);
390 BindingCodec<Map<QName, Object>, Object> newInstance;
391 newInstance = newInstanceOf(newCodec);
392 IdentifierCodecImpl<T> newWrapper = new IdentifierCodecImpl<>(newInstance);
393 identifierCodecs.put(object, newWrapper);
397 @SuppressWarnings("rawtypes")
398 public ChoiceCaseCodecImpl getCaseCodecFor(Class caseClass) {
399 ChoiceCaseCodecImpl<?> potential = caseCodecs.get(caseClass);
400 if (potential != null) {
403 ConcreteType typeref = Types.typeForClass(caseClass);
404 ChoiceCaseCodecImpl caseCodec = typeToCaseCodecs.get(typeref);
406 Preconditions.checkState(caseCodec != null, "Case Codec was not created proactivelly for %s",
407 caseClass.getName());
408 Preconditions.checkState(caseCodec.getSchema() != null, "Case schema is not available for %s",
409 caseClass.getName());
410 @SuppressWarnings("unchecked")
411 Class<? extends BindingCodec> newCodec = generator.caseCodecFor(caseClass, caseCodec.getSchema());
412 BindingCodec newInstance = newInstanceOf(newCodec);
413 caseCodec.setDelegate(newInstance);
414 caseCodecs.put(caseClass, caseCodec);
416 for (Entry<Class<?>, ChoiceCodecImpl<?>> choice : choiceCodecs.entrySet()) {
417 if (choice.getKey().isAssignableFrom(caseClass)) {
418 choice.getValue().cases.put(caseClass, caseCodec);
424 public void onModuleContextAdded(SchemaContext schemaContext, Module module, ModuleContext context) {
425 pathToType.putAll(context.getChildNodes());
426 augmentToType.putAll(context.getTypeToAugmentation().inverse());
427 qnamesToIdentityMap.putAll(context.getIdentities());
428 for (Entry<QName, GeneratedTOBuilder> identity : context.getIdentities().entrySet()) {
430 new ReferencedTypeImpl(identity.getValue().getPackageName(), identity.getValue().getName()),
433 captureCases(context.getCases(), schemaContext);
436 private void captureCases(Map<SchemaPath, GeneratedTypeBuilder> cases, SchemaContext module) {
437 for (Entry<SchemaPath, GeneratedTypeBuilder> caseNode : cases.entrySet()) {
438 ReferencedTypeImpl typeref = new ReferencedTypeImpl(caseNode.getValue().getPackageName(), caseNode
439 .getValue().getName());
441 pathToType.put(caseNode.getKey(), caseNode.getValue());
443 ChoiceCaseNode node = (ChoiceCaseNode) SchemaContextUtil.findDataSchemaNode(module, caseNode.getKey());
446 LOG.warn("Failed to find YANG SchemaNode for {}, with path {} was not found in context.",
447 typeref.getFullyQualifiedName(), caseNode.getKey());
448 @SuppressWarnings("rawtypes")
449 ChoiceCaseCodecImpl value = new ChoiceCaseCodecImpl();
450 typeToCaseCodecs.putIfAbsent(typeref, value);
453 @SuppressWarnings("rawtypes")
454 ChoiceCaseCodecImpl value = new ChoiceCaseCodecImpl(node);
455 typeToCaseCodecs.putIfAbsent(typeref, value);
460 public void onGlobalContextUpdated(SchemaContext context) {
461 currentSchema = context;
464 @SuppressWarnings({ "unchecked", "rawtypes" })
466 public void onChoiceCodecCreated(Class<?> choiceClass,
467 Class<? extends BindingCodec<Map<QName, Object>, Object>> choiceCodec, ChoiceNode schema) {
468 ChoiceCodec<?> oldCodec = choiceCodecs.get(choiceClass);
469 Preconditions.checkState(oldCodec == null);
470 BindingCodec<Map<QName, Object>, Object> delegate = newInstanceOf(choiceCodec);
471 ChoiceCodecImpl<?> newCodec = new ChoiceCodecImpl(delegate);
472 choiceCodecs.put(choiceClass, newCodec);
473 CodecMapping.setClassToCaseMap(choiceCodec, classToCaseRawCodec);
474 CodecMapping.setCompositeNodeToCaseMap(choiceCodec, newCodec.getCompositeToCase());
476 tryToCreateCasesCodecs(schema);
480 private void tryToCreateCasesCodecs(ChoiceNode schema) {
481 for (ChoiceCaseNode caseNode : schema.getCases()) {
482 SchemaPath path = caseNode.getPath();
483 GeneratedTypeBuilder type;
484 if (path != null && (type = pathToType.get(path)) != null) {
485 ReferencedTypeImpl typeref = new ReferencedTypeImpl(type.getPackageName(), type.getName());
486 ChoiceCaseCodecImpl partialCodec = typeToCaseCodecs.get(typeref);
487 if (partialCodec.getSchema() == null) {
488 partialCodec.setSchema(caseNode);
491 Class<?> caseClass = classLoadingStrategy.loadClass(type.getFullyQualifiedName());
492 getCaseCodecFor(caseClass);
493 } catch (ClassNotFoundException e) {
494 LOG.trace("Could not proactivelly create case codec for {}", type, e);
502 public void onValueCodecCreated(Class<?> valueClass, Class<?> valueCodec) {
506 public void onCaseCodecCreated(Class<?> choiceClass,
507 Class<? extends BindingCodec<Map<QName, Object>, Object>> choiceCodec) {
511 public void onDataContainerCodecCreated(Class<?> dataClass, Class<? extends BindingCodec<?, ?>> dataCodec) {
512 if (Augmentable.class.isAssignableFrom(dataClass)) {
513 AugmentableCompositeCodec augmentableCodec = getAugmentableCodec(dataClass);
514 CodecMapping.setAugmentationCodec(dataCodec, augmentableCodec);
519 public AugmentableCompositeCodec getAugmentableCodec(Class<?> dataClass) {
520 AugmentableCompositeCodec ret = augmentableCodecs.get(dataClass);
524 ret = new AugmentableCompositeCodec(dataClass);
525 augmentableCodecs.put(dataClass, ret);
527 Map<Type, SchemaNode> typeToSchemaNode = generator.getTypeToSchemaNode();
528 Type refType = new ReferencedTypeImpl(dataClass.getPackage().getName(), dataClass.getSimpleName());
529 SchemaNode node = typeToSchemaNode.get(refType);
530 tryToLoadAugmentations(node);
535 private void tryToLoadAugmentations(SchemaNode schemaNode) {
536 if (schemaNode instanceof AugmentationTarget) {
537 AugmentationTarget augmentationTarget = (AugmentationTarget) schemaNode;
538 Set<AugmentationSchema> augments = augmentationTarget.getAvailableAugmentations();
539 Set<Type> augmentTypes = new HashSet<>();
540 if (augments != null) {
541 for (AugmentationSchema augment : augments) {
542 Type augmentType = augmentToType.get(augment);
543 if (augmentType == null) {
544 LOG.warn("Failed to find type for augmentation of {}", augment);
546 augmentTypes.add(augmentType);
549 for (Type augmentType : augmentTypes) {
550 Class<? extends Augmentation<?>> clazz = null;
552 clazz = (Class<? extends Augmentation<?>>) classLoadingStrategy.loadClass(augmentType);
553 getCodecForAugmentation(clazz);
554 } catch (ClassNotFoundException e) {
555 LOG.warn("Failed to find class for augmentation of {}, reason: {}", augmentType, e.toString());
556 } catch (CodeGenerationException e) {
557 LOG.warn("Failed to proactively generate augment coded for {}, reason: {}", augmentType, e.toString());
563 if (schemaNode instanceof DataNodeContainer) {
564 Set<DataSchemaNode> childNodes = ((DataNodeContainer) schemaNode).getChildNodes();
565 for (DataSchemaNode child : childNodes) {
566 tryToLoadAugmentations(child);
571 private static abstract class IntermediateCodec<T> implements //
572 DomCodec<T>, Delegator<BindingCodec<Map<QName, Object>, Object>> {
574 private final BindingCodec<Map<QName, Object>, Object> delegate;
577 public BindingCodec<Map<QName, Object>, Object> getDelegate() {
581 public IntermediateCodec(BindingCodec<Map<QName, Object>, Object> delegate) {
582 this.delegate = delegate;
586 public Node<?> serialize(ValueWithQName<T> input) {
587 Map<QName, Object> intermediateOutput = delegate.serialize(input);
588 return IntermediateMapping.toNode(intermediateOutput);
592 private static class IdentifierCodecImpl<T extends Identifier<?>> //
593 extends IntermediateCodec<T> //
594 implements IdentifierCodec<T> {
596 public IdentifierCodecImpl(BindingCodec<Map<QName, Object>, Object> delegate) {
601 public ValueWithQName<T> deserialize(Node<?> input) {
602 QName qname = input.getNodeType();
603 @SuppressWarnings("unchecked")
604 T value = (T) getDelegate().deserialize((Map<QName, Object>) input);
605 return new ValueWithQName<T>(qname, value);
609 public CompositeNode serialize(ValueWithQName<T> input) {
610 return (CompositeNode) super.serialize(input);
614 private static class DataContainerCodecImpl<T extends DataContainer> //
615 extends IntermediateCodec<T> //
616 implements DataContainerCodec<T> {
618 public DataContainerCodecImpl(BindingCodec<Map<QName, Object>, Object> delegate) {
623 public ValueWithQName<T> deserialize(Node<?> input) {
627 QName qname = input.getNodeType();
628 @SuppressWarnings("unchecked")
629 T value = (T) getDelegate().deserialize((Map<QName, Object>) input);
630 return new ValueWithQName<T>(qname, value);
634 public CompositeNode serialize(ValueWithQName<T> input) {
635 return (CompositeNode) super.serialize(input);
639 @SuppressWarnings("rawtypes")
640 private static class ChoiceCaseCodecImpl<T extends DataContainer> implements ChoiceCaseCodec<T>, //
641 Delegator<BindingCodec> {
642 private boolean augmenting;
643 private BindingCodec delegate;
645 private Set<String> validNames;
646 private Set<QName> validQNames;
647 private ChoiceCaseNode schema;
649 public void setSchema(ChoiceCaseNode caseNode) {
650 this.schema = caseNode;
651 validNames = new HashSet<>();
652 validQNames = new HashSet<>();
653 for (DataSchemaNode node : caseNode.getChildNodes()) {
654 QName qname = node.getQName();
655 validQNames.add(qname);
656 validNames.add(qname.getLocalName());
658 augmenting = caseNode.isAugmenting();
661 public ChoiceCaseCodecImpl() {
662 this.delegate = NOT_READY_CODEC;
665 public ChoiceCaseCodecImpl(ChoiceCaseNode caseNode) {
666 this.delegate = NOT_READY_CODEC;
671 public ValueWithQName<T> deserialize(Node<?> input) {
672 throw new UnsupportedOperationException("Direct invocation of this codec is not allowed.");
676 public CompositeNode serialize(ValueWithQName<T> input) {
677 throw new UnsupportedOperationException("Direct invocation of this codec is not allowed.");
681 public BindingCodec getDelegate() {
685 public void setDelegate(BindingCodec delegate) {
686 this.delegate = delegate;
689 public ChoiceCaseNode getSchema() {
694 public boolean isAcceptable(Node<?> input) {
695 if (input instanceof CompositeNode) {
697 return checkAugmenting((CompositeNode) input);
699 return checkLocal((CompositeNode) input);
705 private boolean checkLocal(CompositeNode input) {
706 QName parent = input.getNodeType();
707 for (Node<?> childNode : input.getChildren()) {
708 QName child = childNode.getNodeType();
709 if (!Objects.equals(parent.getNamespace(), child.getNamespace())
710 || !Objects.equals(parent.getRevision(), child.getRevision())) {
713 if (validNames.contains(child.getLocalName())) {
720 private boolean checkAugmenting(CompositeNode input) {
721 for (Node<?> child : input.getChildren()) {
722 if (validQNames.contains(child.getNodeType())) {
730 private static class ChoiceCodecImpl<T> implements ChoiceCodec<T> {
732 private final BindingCodec<Map<QName, Object>, Object> delegate;
734 @SuppressWarnings("rawtypes")
735 private final Map<Class, ChoiceCaseCodecImpl<?>> cases = Collections
736 .synchronizedMap(new WeakHashMap<Class, ChoiceCaseCodecImpl<?>>());
738 private final CaseCompositeNodeMapFacade CompositeToCase;
740 public ChoiceCodecImpl(BindingCodec<Map<QName, Object>, Object> delegate) {
741 this.delegate = delegate;
742 this.CompositeToCase = new CaseCompositeNodeMapFacade(cases);
746 public ValueWithQName<T> deserialize(Node<?> input) {
747 throw new UnsupportedOperationException("Direct invocation of this codec is not allowed.");
751 public Node<?> serialize(ValueWithQName<T> input) {
752 throw new UnsupportedOperationException("Direct invocation of this codec is not allowed.");
755 public CaseCompositeNodeMapFacade getCompositeToCase() {
756 return CompositeToCase;
759 public Map<Class, ChoiceCaseCodecImpl<?>> getCases() {
763 public BindingCodec<Map<QName, Object>, Object> getDelegate() {
769 @SuppressWarnings("rawtypes")
770 private class CaseClassMapFacade extends MapFacadeBase {
773 public Set<Entry<Class, BindingCodec<Object, Object>>> entrySet() {
774 return Collections.emptySet();
778 public BindingCodec get(Object key) {
779 if (key instanceof Class) {
780 Class cls = (Class) key;
781 // bindingClassEncountered(cls);
782 ChoiceCaseCodecImpl caseCodec = getCaseCodecFor(cls);
783 return caseCodec.getDelegate();
789 @SuppressWarnings("rawtypes")
790 private static class CaseCompositeNodeMapFacade extends MapFacadeBase<CompositeNode> {
792 final Map<Class, ChoiceCaseCodecImpl<?>> choiceCases;
794 public CaseCompositeNodeMapFacade(Map<Class, ChoiceCaseCodecImpl<?>> choiceCases) {
795 this.choiceCases = choiceCases;
799 public BindingCodec get(Object key) {
800 if (!(key instanceof CompositeNode)) {
803 for (Entry<Class, ChoiceCaseCodecImpl<?>> entry : choiceCases.entrySet()) {
804 ChoiceCaseCodecImpl<?> codec = entry.getValue();
805 if (codec.isAcceptable((CompositeNode) key)) {
806 return codec.getDelegate();
815 * This map is used as only facade for
816 * {@link org.opendaylight.yangtools.yang.binding.BindingCodec} in different
817 * classloaders to retrieve codec dynamicly based on provided key.
822 @SuppressWarnings("rawtypes")
823 private static abstract class MapFacadeBase<T> implements Map<T, BindingCodec<?, ?>> {
826 public boolean containsKey(Object key) {
827 return get(key) != null;
831 public void clear() {
832 throw notModifiable();
836 public boolean equals(Object obj) {
837 return super.equals(obj);
841 public BindingCodec remove(Object key) {
851 public Collection<BindingCodec<?, ?>> values() {
852 return Collections.emptySet();
855 private UnsupportedOperationException notModifiable() {
856 return new UnsupportedOperationException("Not externally modifiable.");
860 public BindingCodec<Map<QName, Object>, Object> put(T key, BindingCodec<?, ?> value) {
861 throw notModifiable();
865 public void putAll(Map<? extends T, ? extends BindingCodec<?, ?>> m) {
866 throw notModifiable();
870 public int hashCode() {
871 return super.hashCode();
875 public boolean isEmpty() {
880 public Set<T> keySet() {
881 return Collections.emptySet();
885 public Set<Entry<T, BindingCodec<?, ?>>> entrySet() {
886 return Collections.emptySet();
890 public boolean containsValue(Object value) {
895 @SuppressWarnings({ "rawtypes", "unchecked" })
896 private class AugmentableCompositeCodec implements BindingCodec {
898 private final Class augmentableType;
900 Map<Class, AugmentationCodec<?>> localAugmentationCodecs = Collections
901 .synchronizedMap(new WeakHashMap<Class, AugmentationCodec<?>>());
903 public AugmentableCompositeCodec(Class type) {
904 Preconditions.checkArgument(Augmentable.class.isAssignableFrom(type));
905 augmentableType = type;
909 public Object serialize(Object input) {
910 if (input instanceof Augmentable<?>) {
912 Map<Class, Augmentation> augmentations = getAugmentations(input);
913 return serializeImpl(augmentations);
918 private Map<Class, Augmentation> getAugmentations(Object input) {
919 Field augmentationField;
921 augmentationField = input.getClass().getDeclaredField("augmentation");
922 augmentationField.setAccessible(true);
923 Map<Class, Augmentation> augMap = (Map<Class, Augmentation>) augmentationField.get(input);
925 } catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException e) {
926 LOG.debug("Could not read augmentations for {}", input, e);
928 return Collections.emptyMap();
931 private List serializeImpl(Map<Class, Augmentation> input) {
932 List ret = new ArrayList<>();
933 for (Entry<Class, Augmentation> entry : input.entrySet()) {
934 AugmentationCodec codec = getCodecForAugmentation(entry.getKey());
935 CompositeNode node = codec.serialize(new ValueWithQName(null, entry.getValue()));
936 ret.addAll(node.getChildren());
941 public synchronized <T extends Augmentation<?>> void addAugmentationCodec(Class<T> augmentationClass,
942 AugmentationCodec<T> value) {
943 localAugmentationCodecs.put(augmentationClass, value);
947 public Map<Class, Augmentation> deserialize(Object input) {
948 Map<Class, Augmentation> ret = new HashMap<>();
949 if (input instanceof CompositeNode) {
950 List<Entry<Class, AugmentationCodec<?>>> codecs = new ArrayList<>(localAugmentationCodecs.entrySet());
951 for (Entry<Class, AugmentationCodec<?>> codec : codecs) {
952 ValueWithQName<?> value = codec.getValue().deserialize((CompositeNode) input);
953 if (value != null && value.getValue() != null) {
954 ret.put(codec.getKey(), (Augmentation) value.getValue());
961 public Class getAugmentableType() {
962 return augmentableType;
966 @SuppressWarnings({ "rawtypes", "unchecked" })
967 private static class LateMixinCodec implements BindingCodec, Delegator<BindingCodec> {
969 private BindingCodec delegate;
972 public BindingCodec getDelegate() {
973 if (delegate == null) {
974 throw new IllegalStateException("Codec not initialized yet.");
980 public Object deserialize(Object input) {
981 return getDelegate().deserialize(input);
985 public Object serialize(Object input) {
986 return getDelegate().serialize(input);
990 private static class AugmentationCodecWrapper<T extends Augmentation<?>> implements AugmentationCodec<T>,
991 Delegator<BindingCodec> {
993 private final BindingCodec delegate;
994 private final QName augmentationQName;
996 public AugmentationCodecWrapper(BindingCodec<Map<QName, Object>, Object> rawCodec) {
997 this.delegate = rawCodec;
998 this.augmentationQName = BindingReflections.findQName(rawCodec.getClass());
1002 public BindingCodec getDelegate() {
1007 public CompositeNode serialize(ValueWithQName<T> input) {
1008 @SuppressWarnings("unchecked")
1009 List<Map<QName, Object>> rawValues = (List<Map<QName, Object>>) getDelegate().serialize(input);
1010 List<Node<?>> serialized = new ArrayList<>(rawValues.size());
1011 for (Map<QName, Object> val : rawValues) {
1012 serialized.add(IntermediateMapping.toNode(val));
1014 return new CompositeNodeTOImpl(input.getQname(), null, serialized);
1018 @SuppressWarnings("unchecked")
1019 public ValueWithQName<T> deserialize(Node<?> input) {
1020 Object rawCodecValue = getDelegate().deserialize(input);
1021 return new ValueWithQName<T>(input.getNodeType(), (T) rawCodecValue);
1025 public QName getAugmentationQName() {
1026 return augmentationQName;
1030 private class IdentityCompositeCodec implements IdentityCodec {
1033 public Object deserialize(Object input) {
1034 Preconditions.checkArgument(input instanceof QName);
1035 return deserialize((QName) input);
1039 public Class<?> deserialize(QName input) {
1040 Type type = qnamesToIdentityMap.get(input);
1044 ReferencedTypeImpl typeref = new ReferencedTypeImpl(type.getPackageName(), type.getName());
1045 WeakReference<Class> softref = typeToClass.get(typeref);
1046 if (softref == null) {
1049 Class<?> cls = classLoadingStrategy.loadClass(typeref.getFullyQualifiedName());
1054 } catch (Exception e) {
1055 LOG.warn("Identity {} was not deserialized, because of missing class {}", input,
1056 typeref.getFullyQualifiedName());
1060 return softref.get();
1064 public QName serialize(Class input) {
1065 Preconditions.checkArgument(BaseIdentity.class.isAssignableFrom(input));
1066 bindingClassEncountered(input);
1067 QName qname = identityQNames.get(input);
1068 if (qname != null) {
1071 ConcreteType typeref = Types.typeForClass(input);
1072 qname = typeToQname.get(typeref);
1073 if (qname != null) {
1074 identityQNames.put(input, qname);
1080 public Object serialize(Object input) {
1081 Preconditions.checkArgument(input instanceof Class);
1082 return serialize((Class) input);
1086 public boolean isCodecAvailable(Class<? extends DataContainer> cls) {
1087 if (containerCodecs.containsKey(cls)) {
1090 if (identifierCodecs.containsKey(cls)) {
1093 if (choiceCodecs.containsKey(cls)) {
1096 if (caseCodecs.containsKey(cls)) {
1099 if (augmentableCodecs.containsKey(cls)) {
1102 if (augmentationCodecs.containsKey(cls)) {