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.util.AbstractMap.SimpleEntry;
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;
23 import java.util.WeakHashMap;
24 import java.util.concurrent.ConcurrentHashMap;
25 import java.util.concurrent.ConcurrentMap;
27 import org.opendaylight.yangtools.binding.generator.util.ReferencedTypeImpl;
28 import org.opendaylight.yangtools.binding.generator.util.Types;
29 import org.opendaylight.yangtools.concepts.Delegator;
30 import org.opendaylight.yangtools.concepts.Identifiable;
31 import org.opendaylight.yangtools.sal.binding.generator.api.ClassLoadingStrategy;
32 import org.opendaylight.yangtools.sal.binding.generator.util.CodeGenerationException;
33 import org.opendaylight.yangtools.sal.binding.model.api.ConcreteType;
34 import org.opendaylight.yangtools.sal.binding.model.api.Type;
35 import org.opendaylight.yangtools.sal.binding.model.api.type.builder.GeneratedTOBuilder;
36 import org.opendaylight.yangtools.yang.binding.Augmentable;
37 import org.opendaylight.yangtools.yang.binding.Augmentation;
38 import org.opendaylight.yangtools.yang.binding.BaseIdentity;
39 import org.opendaylight.yangtools.yang.binding.BindingCodec;
40 import org.opendaylight.yangtools.yang.binding.DataContainer;
41 import org.opendaylight.yangtools.yang.binding.DataObject;
42 import org.opendaylight.yangtools.yang.binding.Identifier;
43 import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
44 import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
45 import org.opendaylight.yangtools.yang.common.QName;
46 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
47 import org.opendaylight.yangtools.yang.data.api.Node;
48 import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl;
49 import org.opendaylight.yangtools.yang.data.impl.codec.AugmentationCodec;
50 import org.opendaylight.yangtools.yang.data.impl.codec.ChoiceCaseCodec;
51 import org.opendaylight.yangtools.yang.data.impl.codec.ChoiceCodec;
52 import org.opendaylight.yangtools.yang.data.impl.codec.CodecRegistry;
53 import org.opendaylight.yangtools.yang.data.impl.codec.DataContainerCodec;
54 import org.opendaylight.yangtools.yang.data.impl.codec.DomCodec;
55 import org.opendaylight.yangtools.yang.data.impl.codec.IdentifierCodec;
56 import org.opendaylight.yangtools.yang.data.impl.codec.IdentityCodec;
57 import org.opendaylight.yangtools.yang.data.impl.codec.InstanceIdentifierCodec;
58 import org.opendaylight.yangtools.yang.data.impl.codec.ValueWithQName;
59 import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
60 import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
61 import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
62 import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
63 import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
64 import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
65 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
66 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
67 import org.opendaylight.yangtools.yang.model.api.Module;
68 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
69 import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
70 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
71 import org.slf4j.Logger;
72 import org.slf4j.LoggerFactory;
74 import com.google.common.base.Optional;
75 import com.google.common.base.Preconditions;
76 import com.google.common.collect.BiMap;
77 import com.google.common.collect.HashBiMap;
78 import com.google.common.collect.HashMultimap;
79 import com.google.common.collect.Iterables;
80 import com.google.common.collect.Multimap;
81 import com.google.common.collect.Multimaps;
83 class LazyGeneratedCodecRegistry implements //
85 SchemaContextListener, //
88 private static final Logger LOG = LoggerFactory.getLogger(LazyGeneratedCodecRegistry.class);
90 // Concrete class to codecs
91 private static final Map<Class<?>, DataContainerCodec<?>> containerCodecs = Collections
92 .synchronizedMap(new WeakHashMap<Class<?>, DataContainerCodec<?>>());
93 private static final Map<Class<?>, IdentifierCodec<?>> identifierCodecs = Collections
94 .synchronizedMap(new WeakHashMap<Class<?>, IdentifierCodec<?>>());
95 private static final Map<Class<?>, PublicChoiceCodecImpl<?>> choiceCodecs = Collections
96 .synchronizedMap(new WeakHashMap<Class<?>, PublicChoiceCodecImpl<?>>());
97 private static final Map<Class<?>, ChoiceCaseCodecImpl<?>> caseCodecs = Collections
98 .synchronizedMap(new WeakHashMap<Class<?>, ChoiceCaseCodecImpl<?>>());
99 private static final Map<Class<?>, AugmentableDispatchCodec> augmentableCodecs = Collections
100 .synchronizedMap(new WeakHashMap<Class<?>, AugmentableDispatchCodec>());
101 private static final Map<Class<?>, AugmentationCodecWrapper<?>> augmentationCodecs = Collections
102 .synchronizedMap(new WeakHashMap<Class<?>, AugmentationCodecWrapper<?>>());
103 private static final Map<Class<?>, QName> identityQNames = Collections
104 .synchronizedMap(new WeakHashMap<Class<?>, QName>());
105 private static final Map<QName, Type> qnamesToIdentityMap = new ConcurrentHashMap<>();
106 /** Binding type to encountered classes mapping **/
107 @SuppressWarnings("rawtypes")
108 private static final Map<Type, WeakReference<Class>> typeToClass = new ConcurrentHashMap<>();
110 private static final ConcurrentMap<Type, ChoiceCaseNode> caseTypeToCaseSchema = new ConcurrentHashMap<>();
112 private static final Map<SchemaPath, Type> pathToType = new ConcurrentHashMap<>();
113 private static final Map<List<QName>, Type> pathToInstantiatedType = new ConcurrentHashMap<>();
114 private static final Map<Type, QName> typeToQname = new ConcurrentHashMap<>();
115 private static final BiMap<Type, AugmentationSchema> typeToAugment = HashBiMap
116 .create(new ConcurrentHashMap<Type, AugmentationSchema>());
118 private static final Multimap<Type, Type> augmentableToAugmentations = Multimaps.synchronizedMultimap(HashMultimap
119 .<Type, Type> create());
120 private static final Multimap<Type, Type> choiceToCases = Multimaps.synchronizedMultimap(HashMultimap
121 .<Type, Type> create());
123 private final InstanceIdentifierCodec instanceIdentifierCodec = new InstanceIdentifierCodecImpl(this);
124 private final IdentityCompositeCodec identityRefCodec = new IdentityCompositeCodec();
125 private final ClassLoadingStrategy classLoadingStrategy;
126 private final AbstractTransformerGenerator generator;
127 private final SchemaLock lock;
129 // FIXME: how is this protected?
130 private SchemaContext currentSchema;
132 LazyGeneratedCodecRegistry(final SchemaLock lock, final AbstractTransformerGenerator generator,
133 final ClassLoadingStrategy classLoadingStrategy) {
134 this.lock = Preconditions.checkNotNull(lock);
135 this.classLoadingStrategy = Preconditions.checkNotNull(classLoadingStrategy);
136 this.generator = Preconditions.checkNotNull(generator);
139 public SchemaLock getLock() {
144 public InstanceIdentifierCodec getInstanceIdentifierCodec() {
145 return instanceIdentifierCodec;
148 @SuppressWarnings("unchecked")
150 public <T extends Augmentation<?>> AugmentationCodecWrapper<T> getCodecForAugmentation(final Class<T> augClass) {
151 AugmentationCodecWrapper<T> codec = null;
152 @SuppressWarnings("rawtypes")
153 AugmentationCodecWrapper potentialCodec = augmentationCodecs.get(augClass);
154 if (potentialCodec != null) {
155 codec = potentialCodec;
157 lock.waitForSchema(augClass);
158 Class<? extends BindingCodec<Map<QName, Object>, Object>> augmentRawCodec = generator
159 .augmentationTransformerFor(augClass);
161 BindingCodec<Map<QName, Object>, Object> rawCodec = newInstanceOf(augmentRawCodec);
162 codec = new AugmentationCodecWrapper<T>(rawCodec, augClass);
163 augmentationCodecs.put(augClass, codec);
166 final Class<? extends Augmentable<?>> objectSupertype;
168 objectSupertype = BindingReflections.findAugmentationTarget(augClass);
169 } catch (Exception e) {
170 LOG.warn("Failed to find target for augmentation {}, ignoring it", augClass, e);
174 if (objectSupertype == null) {
175 LOG.warn("Augmentation target for {} not found, ignoring it", augClass);
179 getAugmentableCodec(objectSupertype).addImplementation(codec);
183 @SuppressWarnings("unchecked")
185 public QName getQNameForAugmentation(final Class<?> cls) {
186 Preconditions.checkArgument(Augmentation.class.isAssignableFrom(cls));
187 return getCodecForAugmentation((Class<? extends Augmentation<?>>) cls).getAugmentationQName();
191 public Class<?> getClassForPath(final List<QName> names) {
192 final DataSchemaNode node = getSchemaNode(names);
193 final SchemaPath path = node.getPath();
194 final Type t = pathToType.get(path);
198 type = new ReferencedTypeImpl(t.getPackageName(), t.getName());
200 type = pathToInstantiatedType.get(names);
201 Preconditions.checkState(type != null, "Failed to lookup instantiated type for path %s", path);
204 @SuppressWarnings("rawtypes")
205 final WeakReference<Class> weakRef = typeToClass.get(type);
206 Preconditions.checkState(weakRef != null, "Could not find loaded class for path: %s and type: %s", path,
207 type.getFullyQualifiedName());
208 return weakRef.get();
212 public void putPathToClass(final List<QName> names, final Class<?> cls) {
213 final Type reference = Types.typeForClass(cls);
214 pathToInstantiatedType.put(names, reference);
215 LOG.trace("Path {} attached to class {} reference {}", names, cls, reference);
216 bindingClassEncountered(cls);
220 public IdentifierCodec<?> getKeyCodecForPath(final List<QName> names) {
221 @SuppressWarnings("unchecked")
222 Class<? extends Identifiable<?>> cls = (Class<? extends Identifiable<?>>) getClassForPath(names);
223 return getIdentifierCodecForIdentifiable(cls);
227 public <T extends DataContainer> DataContainerCodec<T> getCodecForDataObject(final Class<T> type) {
228 @SuppressWarnings("unchecked")
229 DataContainerCodec<T> ret = (DataContainerCodec<T>) containerCodecs.get(type);
233 Class<? extends BindingCodec<Map<QName, Object>, Object>> newType = generator.transformerFor(type);
234 BindingCodec<Map<QName, Object>, Object> rawCodec = newInstanceOf(newType);
235 DataContainerCodecImpl<T> newWrapper = new DataContainerCodecImpl<>(rawCodec);
236 containerCodecs.put(type, newWrapper);
241 @SuppressWarnings("rawtypes")
242 public void bindingClassEncountered(final Class cls) {
244 ConcreteType typeRef = Types.typeForClass(cls);
245 if (typeToClass.containsKey(typeRef)) {
248 LOG.trace("Binding Class {} encountered.", cls);
249 WeakReference<Class> weakRef = new WeakReference<>(cls);
250 typeToClass.put(typeRef, weakRef);
251 if (Augmentation.class.isAssignableFrom(cls)) {
253 } else if (DataObject.class.isAssignableFrom(cls)) {
254 @SuppressWarnings({ "unchecked", "unused" })
255 Object cdc = getCodecForDataObject((Class<? extends DataObject>) cls);
260 public void onClassProcessed(final Class<?> cls) {
261 ConcreteType typeRef = Types.typeForClass(cls);
262 if (typeToClass.containsKey(typeRef)) {
265 LOG.trace("Binding Class {} encountered.", cls);
266 @SuppressWarnings("rawtypes")
267 WeakReference<Class> weakRef = new WeakReference<Class>(cls);
268 typeToClass.put(typeRef, weakRef);
271 private DataSchemaNode getSchemaNode(final List<QName> path) {
272 QName firstNode = path.get(0);
273 DataNodeContainer previous = currentSchema.findModuleByNamespaceAndRevision(firstNode.getNamespace(),
274 firstNode.getRevision());
275 Preconditions.checkArgument(previous != null, "Failed to find module %s for path %s", firstNode, path);
277 Iterator<QName> iterator = path.iterator();
278 while (iterator.hasNext()) {
279 QName arg = iterator.next();
280 DataSchemaNode currentNode = previous.getDataChildByName(arg);
281 if (currentNode == null && previous instanceof DataNodeContainer) {
282 currentNode = searchInChoices(previous, arg);
284 if (currentNode instanceof DataNodeContainer) {
285 previous = (DataNodeContainer) currentNode;
286 } else if (currentNode instanceof LeafSchemaNode || currentNode instanceof LeafListSchemaNode) {
287 Preconditions.checkState(!iterator.hasNext(), "Path tries to nest inside leaf node.");
291 return (DataSchemaNode) previous;
294 private DataSchemaNode searchInChoices(final DataNodeContainer node, final QName arg) {
295 Set<DataSchemaNode> children = node.getChildNodes();
296 for (DataSchemaNode child : children) {
297 if (child instanceof ChoiceNode) {
298 ChoiceNode choiceNode = (ChoiceNode) child;
299 DataSchemaNode potential = searchInCases(choiceNode, arg);
300 if (potential != null) {
308 private DataSchemaNode searchInCases(final ChoiceNode choiceNode, final QName arg) {
309 Set<ChoiceCaseNode> cases = choiceNode.getCases();
310 for (ChoiceCaseNode caseNode : cases) {
311 DataSchemaNode node = caseNode.getDataChildByName(arg);
319 private static <T> T newInstanceOf(final Class<?> cls) {
321 @SuppressWarnings("unchecked")
322 T ret = (T) cls.newInstance();
324 } catch (InstantiationException e) {
325 LOG.error("Failed to instantiate codec {}", cls.getSimpleName(), e);
326 throw new IllegalStateException(String.format("Failed to instantiate codec %s", cls), e);
327 } catch (IllegalAccessException e) {
329 "Run-time consistency issue: constructor for {} is not available. This indicates either a code generation bug or a misconfiguration of JVM.",
330 cls.getSimpleName(), e);
331 throw new IllegalStateException(String.format("Cannot access contructor of %s", cls), e);
336 public <T extends Identifiable<?>> IdentifierCodec<?> getIdentifierCodecForIdentifiable(final Class<T> type) {
337 IdentifierCodec<?> obj = identifierCodecs.get(type);
341 Class<? extends BindingCodec<Map<QName, Object>, Object>> newCodec = generator
342 .keyTransformerForIdentifiable(type);
343 BindingCodec<Map<QName, Object>, Object> newInstance;
344 newInstance = newInstanceOf(newCodec);
345 IdentifierCodecImpl<?> newWrapper = new IdentifierCodecImpl<>(newInstance);
346 identifierCodecs.put(type, newWrapper);
351 public IdentityCodec<?> getIdentityCodec() {
352 return identityRefCodec;
355 @SuppressWarnings("unchecked")
357 public <T extends BaseIdentity> IdentityCodec<T> getCodecForIdentity(final Class<T> codec) {
358 bindingClassEncountered(codec);
359 return identityRefCodec;
363 public void onCodecCreated(final Class<?> cls) {
364 CodecMapping.setIdentifierCodec(cls, instanceIdentifierCodec);
365 CodecMapping.setIdentityRefCodec(cls, identityRefCodec);
369 public <T extends Identifier<?>> IdentifierCodec<T> getCodecForIdentifier(final Class<T> object) {
370 @SuppressWarnings("unchecked")
371 IdentifierCodec<T> obj = (IdentifierCodec<T>) identifierCodecs.get(object);
375 Class<? extends BindingCodec<Map<QName, Object>, Object>> newCodec = generator
376 .keyTransformerForIdentifier(object);
377 BindingCodec<Map<QName, Object>, Object> newInstance;
378 newInstance = newInstanceOf(newCodec);
379 IdentifierCodecImpl<T> newWrapper = new IdentifierCodecImpl<>(newInstance);
380 identifierCodecs.put(object, newWrapper);
384 @SuppressWarnings("rawtypes")
385 public ChoiceCaseCodecImpl getCaseCodecFor(final Class caseClass) {
386 ChoiceCaseCodecImpl<?> potential = caseCodecs.get(caseClass);
387 if (potential != null) {
390 ConcreteType typeref = Types.typeForClass(caseClass);
391 ChoiceCaseNode caseSchema = caseTypeToCaseSchema.get(typeref);
393 Preconditions.checkState(caseSchema != null, "Case schema is not available for %s", caseClass.getName());
394 Class<? extends BindingCodec> newCodec = generator.caseCodecFor(caseClass, caseSchema);
395 BindingCodec newInstance = newInstanceOf(newCodec);
396 ChoiceCaseCodecImpl caseCodec = new ChoiceCaseCodecImpl(caseClass, caseSchema, newInstance);
397 caseCodecs.put(caseClass, caseCodec);
399 for (Entry<Class<?>, PublicChoiceCodecImpl<?>> choice : choiceCodecs.entrySet()) {
400 if (choice.getKey().isAssignableFrom(caseClass)) {
401 choice.getValue().cases.put(caseClass, caseCodec);
407 public void onModuleContextAdded(final SchemaContext schemaContext, final Module module, final ModuleContext context) {
408 pathToType.putAll(context.getChildNodes());
410 BiMap<Type, AugmentationSchema> bimap = context.getTypeToAugmentation();
411 for (Map.Entry<Type, AugmentationSchema> entry : bimap.entrySet()) {
412 Type key = entry.getKey();
413 AugmentationSchema value = entry.getValue();
414 Set<DataSchemaNode> augmentedNodes = value.getChildNodes();
415 if (augmentedNodes != null && !(augmentedNodes.isEmpty())) {
416 typeToAugment.put(key, value);
420 qnamesToIdentityMap.putAll(context.getIdentities());
421 for (Entry<QName, GeneratedTOBuilder> identity : context.getIdentities().entrySet()) {
423 new ReferencedTypeImpl(identity.getValue().getPackageName(), identity.getValue().getName()),
427 synchronized (augmentableToAugmentations) {
428 augmentableToAugmentations.putAll(context.getAugmentableToAugmentations());
430 synchronized (choiceToCases) {
431 choiceToCases.putAll(context.getChoiceToCases());
433 synchronized (caseTypeToCaseSchema) {
434 caseTypeToCaseSchema.putAll(context.getCaseTypeToSchemas());
439 public void onGlobalContextUpdated(final SchemaContext context) {
440 currentSchema = context;
443 @SuppressWarnings({ "unchecked", "rawtypes" })
445 public void onChoiceCodecCreated(final Class<?> choiceClass,
446 final Class<? extends BindingCodec<Map<QName, Object>, Object>> choiceCodec, final ChoiceNode schema) {
447 ChoiceCodec<?> oldCodec = choiceCodecs.get(choiceClass);
448 Preconditions.checkState(oldCodec == null);
449 BindingCodec<Map<QName, Object>, Object> delegate = newInstanceOf(choiceCodec);
450 PublicChoiceCodecImpl<?> newCodec = new PublicChoiceCodecImpl(delegate);
451 DispatchChoiceCodecImpl dispatchCodec = new DispatchChoiceCodecImpl(choiceClass);
452 choiceCodecs.put(choiceClass, newCodec);
453 CodecMapping.setDispatchCodec(choiceCodec, dispatchCodec);
457 public void onValueCodecCreated(final Class<?> valueClass, final Class<?> valueCodec) {
461 public void onCaseCodecCreated(final Class<?> choiceClass,
462 final Class<? extends BindingCodec<Map<QName, Object>, Object>> choiceCodec) {
466 public void onDataContainerCodecCreated(final Class<?> dataClass,
467 final Class<? extends BindingCodec<?, ?>> dataCodec) {
468 if (Augmentable.class.isAssignableFrom(dataClass)) {
469 AugmentableDispatchCodec augmentableCodec = getAugmentableCodec(dataClass);
470 CodecMapping.setAugmentationCodec(dataCodec, augmentableCodec);
474 public synchronized AugmentableDispatchCodec getAugmentableCodec(final Class<?> dataClass) {
475 AugmentableDispatchCodec ret = augmentableCodecs.get(dataClass);
479 ret = new AugmentableDispatchCodec(dataClass);
480 augmentableCodecs.put(dataClass, ret);
481 ret.tryToLoadImplementations();
485 private static abstract class IntermediateCodec<T> implements //
486 DomCodec<T>, Delegator<BindingCodec<Map<QName, Object>, Object>> {
488 private final BindingCodec<Map<QName, Object>, Object> delegate;
491 public BindingCodec<Map<QName, Object>, Object> getDelegate() {
495 public IntermediateCodec(final BindingCodec<Map<QName, Object>, Object> delegate) {
496 this.delegate = delegate;
500 public Node<?> serialize(final ValueWithQName<T> input) {
501 Map<QName, Object> intermediateOutput = delegate.serialize(input);
502 return IntermediateMapping.toNode(intermediateOutput);
507 private static class IdentifierCodecImpl<T extends Identifier<?>> //
508 extends IntermediateCodec<T> //
509 implements IdentifierCodec<T> {
511 public IdentifierCodecImpl(final BindingCodec<Map<QName, Object>, Object> delegate) {
516 public ValueWithQName<T> deserialize(final Node<?> input) {
517 QName qname = input.getNodeType();
518 @SuppressWarnings("unchecked")
519 T value = (T) getDelegate().deserialize((Map<QName, Object>) input);
520 return new ValueWithQName<T>(qname, value);
524 public ValueWithQName<T> deserialize(final Node<?> input, final InstanceIdentifier<?> bindingIdentifier) {
525 QName qname = input.getNodeType();
526 @SuppressWarnings("unchecked")
527 T value = (T) getDelegate().deserialize((Map<QName, Object>) input, bindingIdentifier);
528 return new ValueWithQName<T>(qname, value);
532 public CompositeNode serialize(final ValueWithQName<T> input) {
533 return (CompositeNode) super.serialize(input);
537 private static class DataContainerCodecImpl<T extends DataContainer> //
538 extends IntermediateCodec<T> //
539 implements DataContainerCodec<T> {
541 public DataContainerCodecImpl(final BindingCodec<Map<QName, Object>, Object> delegate) {
546 public ValueWithQName<T> deserialize(final Node<?> input) {
550 QName qname = input.getNodeType();
551 @SuppressWarnings("unchecked")
552 T value = (T) getDelegate().deserialize((Map<QName, Object>) input);
553 return new ValueWithQName<T>(qname, value);
557 public ValueWithQName<T> deserialize(final Node<?> input, final InstanceIdentifier<?> bindingIdentifier) {
561 QName qname = input.getNodeType();
562 @SuppressWarnings("unchecked")
563 T value = (T) getDelegate().deserialize((Map<QName, Object>) input, bindingIdentifier);
564 return new ValueWithQName<T>(qname, value);
568 public CompositeNode serialize(final ValueWithQName<T> input) {
569 return (CompositeNode) super.serialize(input);
573 private interface LocationAwareBindingCodec<P, I> extends BindingCodec<P, I> {
575 boolean isApplicable(InstanceIdentifier<?> location);
577 public Class<?> getDataType();
581 @SuppressWarnings("rawtypes")
582 private abstract class LocationAwareDispatchCodec<T extends LocationAwareBindingCodec> implements BindingCodec {
584 private final Map<Class, T> implementations = Collections.synchronizedMap(new WeakHashMap<Class, T>());
585 private final Set<InstanceIdentifier<?>> adaptedForPaths = new HashSet<>();
587 protected Map<Class, T> getImplementations() {
588 return implementations;
591 protected void addImplementation(final T implementation) {
592 implementations.put(implementation.getDataType(), implementation);
596 public final Object deserialize(final Object input) {
597 throw new UnsupportedOperationException("Invocation of deserialize without Tree location is unsupported");
601 public Object serialize(final Object input) {
602 Preconditions.checkArgument(input instanceof DataContainer);
603 Class<? extends DataContainer> inputType = ((DataContainer) input).getImplementedInterface();
604 T implementation = implementations.get(inputType);
605 if (implementation == null) {
606 implementation = tryToLoadImplementationImpl(inputType);
612 private T tryToLoadImplementationImpl(final Class<? extends DataContainer> inputType) {
613 T implementation = tryToLoadImplementation(inputType);
614 Preconditions.checkArgument(implementation != null, "Data type %s is not supported.", inputType);
615 addImplementation(implementation);
616 return implementation;
619 protected final synchronized void adaptForPath(final InstanceIdentifier<?> path) {
620 if (adaptedForPaths.contains(path)) {
624 * We search in schema context if the use of this location aware
625 * codec (augmentable codec, case codec) makes sense on provided
629 Optional<DataNodeContainer> contextNode = BindingSchemaContextUtils.findDataNodeContainer(currentSchema,
632 * If context node is present, this codec makes sense on provided
636 if (contextNode.isPresent()) {
637 synchronized (this) {
640 * We adapt (turn on / off) possible implementations of
641 * child codecs (augmentations, cases) based on this
647 adaptForPathImpl(path, contextNode.get());
650 * We trigger serialization of instance identifier, to
651 * make sure instance identifier codec is aware of
652 * combination of this path / augmentation / case
654 instanceIdentifierCodec.serialize(path);
655 } catch (Exception e) {
656 LOG.warn("Exception during preparation of instance identifier codec for path {}.", path, e);
658 adaptedForPaths.add(path);
661 LOG.debug("Context node (parent node) not found for {}", path);
665 protected abstract T tryToLoadImplementation(Class<? extends DataContainer> inputType);
667 protected abstract void tryToLoadImplementations();
669 protected abstract void adaptForPathImpl(InstanceIdentifier<?> path, DataNodeContainer ctx);
672 @SuppressWarnings("rawtypes")
673 private static class ChoiceCaseCodecImpl<T extends DataContainer> implements ChoiceCaseCodec<T>, //
674 Delegator<BindingCodec>, LocationAwareBindingCodec<Node<?>, ValueWithQName<T>> {
675 private final BindingCodec delegate;
676 private final ChoiceCaseNode schema;
677 private final Map<InstanceIdentifier<?>, ChoiceCaseNode> instantiatedLocations;
678 private final Class<?> dataType;
681 public boolean isApplicable(final InstanceIdentifier location) {
682 return instantiatedLocations.containsKey(location);
685 public ChoiceCaseCodecImpl(final Class<?> caseClass, final ChoiceCaseNode caseNode, final BindingCodec newInstance) {
686 this.delegate = newInstance;
687 this.dataType = caseClass;
688 this.schema = caseNode;
689 instantiatedLocations = new HashMap<>();
693 public ValueWithQName<T> deserialize(final Node<?> input) {
694 throw new UnsupportedOperationException("Direct invocation of this codec is not allowed.");
698 public ValueWithQName<T> deserialize(final Node<?> input, final InstanceIdentifier<?> bindingIdentifier) {
702 QName qname = input.getNodeType();
703 synchronized (instantiatedLocations) {
704 ChoiceCaseNode instantiation = instantiatedLocations.get(bindingIdentifier);
705 if (instantiation != null) {
706 qname = instantiatedLocations.get(bindingIdentifier).getQName();
709 @SuppressWarnings("unchecked")
710 T value = (T) getDelegate().deserialize(new SimpleEntry(qname, input), bindingIdentifier);
711 return new ValueWithQName<T>(qname, value);
715 public CompositeNode serialize(final ValueWithQName<T> input) {
716 throw new UnsupportedOperationException("Direct invocation of this codec is not allowed.");
720 public BindingCodec getDelegate() {
724 public ChoiceCaseNode getSchema() {
730 public boolean isAcceptable(final Node<?> input) {
731 return checkAgainstSchema(schema, input);
734 private static boolean checkAgainstSchema(final ChoiceCaseNode schema, final Node<?> node) {
735 if (node instanceof CompositeNode) {
736 CompositeNode input = (CompositeNode) node;
737 for (Node<?> childNode : input.getValue()) {
738 QName child = childNode.getNodeType();
739 if (schema.getDataChildByName(child) != null) {
748 public Class<?> getDataType() {
752 public void adaptForPath(final InstanceIdentifier<?> augTarget, final ChoiceCaseNode choiceCaseNode) {
753 synchronized (instantiatedLocations) {
754 instantiatedLocations.put(augTarget, choiceCaseNode);
758 public boolean isAcceptable(final InstanceIdentifier path, final CompositeNode input) {
759 ChoiceCaseNode instantiatedSchema = null;
760 synchronized (instantiatedLocations) {
761 instantiatedSchema = instantiatedLocations.get(path);
763 if (instantiatedSchema == null) {
766 return checkAgainstSchema(instantiatedSchema, input);
770 private static class PublicChoiceCodecImpl<T> implements ChoiceCodec<T>,
771 Delegator<BindingCodec<Map<QName, Object>, Object>> {
773 private final BindingCodec<Map<QName, Object>, Object> delegate;
775 @SuppressWarnings("rawtypes")
776 private final Map<Class, ChoiceCaseCodecImpl<?>> cases = Collections
777 .synchronizedMap(new WeakHashMap<Class, ChoiceCaseCodecImpl<?>>());
779 public PublicChoiceCodecImpl(final BindingCodec<Map<QName, Object>, Object> delegate) {
780 this.delegate = delegate;
784 public ValueWithQName<T> deserialize(final Node<?> input) {
785 throw new UnsupportedOperationException("Direct invocation of this codec is not allowed.");
789 public ValueWithQName<T> deserialize(final Node<?> input, final InstanceIdentifier<?> bindingIdentifier) {
790 throw new UnsupportedOperationException("Direct invocation of this codec is not allowed.");
794 public Node<?> serialize(final ValueWithQName<T> input) {
795 throw new UnsupportedOperationException("Direct invocation of this codec is not allowed.");
799 public BindingCodec<Map<QName, Object>, Object> getDelegate() {
805 class DispatchChoiceCodecImpl extends LocationAwareDispatchCodec<ChoiceCaseCodecImpl<?>> {
806 private final Class<?> choiceType;
807 private final QName choiceName;
809 private DispatchChoiceCodecImpl(final Class<?> type) {
811 choiceName = BindingReflections.findQName(type);
815 public Object deserialize(final Object input, @SuppressWarnings("rawtypes") final InstanceIdentifier path) {
818 if (input instanceof CompositeNode) {
819 List<Entry<Class, ChoiceCaseCodecImpl<?>>> codecs = new ArrayList<>(getImplementations().entrySet());
820 for (Entry<Class, ChoiceCaseCodecImpl<?>> codec : codecs) {
821 ChoiceCaseCodecImpl<?> caseCodec = codec.getValue();
822 if (caseCodec.isAcceptable(path, (CompositeNode) input)) {
823 ValueWithQName<?> value = caseCodec.deserialize((CompositeNode) input, path);
825 return value.getValue();
834 @SuppressWarnings("unchecked")
836 public Object serialize(final Object input) {
837 Preconditions.checkArgument(input instanceof Map.Entry<?, ?>, "Input must be QName, Value");
838 @SuppressWarnings("rawtypes")
839 Object inputValue = ((Map.Entry) input).getValue();
840 Preconditions.checkArgument(inputValue instanceof DataObject);
841 Class<? extends DataContainer> inputType = ((DataObject) inputValue).getImplementedInterface();
842 ChoiceCaseCodecImpl<?> codec = tryToLoadImplementation(inputType);
843 Preconditions.checkState(codec != null, "Unable to get codec for %s", inputType);
844 if(isAugmenting(codec.getSchema())) {
845 // If choice is augmenting we use QName which defined this augmentation
846 return codec.getDelegate().serialize(new ValueWithQName<>(codec.getSchema().getQName(), inputValue));
848 return codec.getDelegate().serialize(input);
851 private boolean isAugmenting(final ChoiceCaseNode schema) {
852 if(schema.isAugmenting()) {
855 QName parentQName = Iterables.get(schema.getPath().getPathTowardsRoot(), 2); // choice QName
856 if(!parentQName.getNamespace().equals(schema.getQName().getNamespace())) {
862 @SuppressWarnings("rawtypes")
863 protected Optional<ChoiceCaseCodecImpl> tryToLoadImplementation(final Type potential) {
865 @SuppressWarnings("unchecked")
866 Class<? extends DataContainer> clazz = (Class<? extends DataContainer>) classLoadingStrategy
867 .loadClass(potential);
868 ChoiceCaseCodecImpl codec = tryToLoadImplementation(clazz);
869 addImplementation(codec);
870 return Optional.of(codec);
871 } catch (ClassNotFoundException e) {
872 LOG.warn("Failed to find class for choice {}", potential, e);
874 return Optional.absent();
878 protected ChoiceCaseCodecImpl<?> tryToLoadImplementation(final Class<? extends DataContainer> inputType) {
879 ChoiceCaseCodecImpl<?> codec = getCaseCodecFor(inputType);
880 addImplementation(codec);
885 protected void tryToLoadImplementations() {
886 Type type = referencedType(choiceType);
887 Collection<Type> potentialCases;
888 synchronized (choiceToCases) {
889 potentialCases = choiceToCases.get(type);
891 for (Type potential : potentialCases) {
893 tryToLoadImplementation(potential);
894 } catch (CodeGenerationException e) {
895 LOG.warn("Failed to proactively generate choice code for {}", type, e);
901 protected void adaptForPathImpl(final InstanceIdentifier<?> augTarget, final DataNodeContainer ctxNode) {
902 Optional<ChoiceNode> newChoice = findInstantiatedChoice(ctxNode, choiceName);
903 tryToLoadImplementations();
904 if (newChoice.isPresent()) {
905 for (@SuppressWarnings("rawtypes")
906 Entry<Class, ChoiceCaseCodecImpl<?>> codec : getImplementations().entrySet()) {
907 ChoiceCaseCodecImpl<?> caseCodec = codec.getValue();
908 Optional<ChoiceCaseNode> instantiatedSchema = findInstantiatedCase(newChoice.get(),
909 caseCodec.getSchema());
910 if (instantiatedSchema.isPresent()) {
911 caseCodec.adaptForPath(augTarget, instantiatedSchema.get());
917 private Optional<ChoiceNode> findInstantiatedChoice(final DataNodeContainer ctxNode, final QName choiceName) {
918 DataSchemaNode potential = ctxNode.getDataChildByName(choiceName);
919 if (potential == null) {
920 potential = ctxNode.getDataChildByName(choiceName.getLocalName());
923 if (potential instanceof ChoiceNode) {
924 return Optional.of((ChoiceNode) potential);
927 return Optional.absent();
930 private Optional<ChoiceCaseNode> findInstantiatedCase(final ChoiceNode newChoice, final ChoiceCaseNode schema) {
931 ChoiceCaseNode potential = newChoice.getCaseNodeByName(schema.getQName());
932 if (potential != null) {
933 return Optional.of(potential);
935 // FIXME: Probably requires more extensive check
936 // e.g. we have one choice and two augmentations from different
937 // modules using same local name
938 // but different namespace / contents
939 return Optional.fromNullable(newChoice.getCaseNodeByName(schema.getQName().getLocalName()));
943 @SuppressWarnings({ "rawtypes", "unchecked" })
944 class AugmentableDispatchCodec extends LocationAwareDispatchCodec<AugmentationCodecWrapper> {
946 private final Class augmentableType;
948 public AugmentableDispatchCodec(final Class type) {
949 Preconditions.checkArgument(Augmentable.class.isAssignableFrom(type));
950 augmentableType = type;
954 // TODO deprecate use without iid
955 public Object serialize(final Object input) {
956 if (input instanceof Augmentable<?>) {
957 Map<Class, Augmentation> augmentations = getAugmentations(input);
958 return serializeImpl(augmentations);
963 private Map<Class, Augmentation> getAugmentations(final Object input) {
964 Field augmentationField;
966 augmentationField = input.getClass().getDeclaredField("augmentation");
967 augmentationField.setAccessible(true);
968 Map<Class, Augmentation> augMap = (Map<Class, Augmentation>) augmentationField.get(input);
970 } catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException e) {
971 LOG.debug("Could not read augmentations for {}", input, e);
973 return Collections.emptyMap();
976 @SuppressWarnings("deprecation")
977 private List serializeImpl(final Map<Class, Augmentation> input) {
978 List ret = new ArrayList<>();
979 for (Entry<Class, Augmentation> entry : input.entrySet()) {
980 AugmentationCodec codec = getCodecForAugmentation(entry.getKey());
981 CompositeNode node = codec.serialize(new ValueWithQName(null, entry.getValue()));
982 ret.addAll(node.getChildren());
988 public Map<Class, Augmentation> deserialize(final Object input, final InstanceIdentifier path) {
990 Map<Class, Augmentation> ret = new HashMap<>();
992 if (input instanceof CompositeNode) {
993 List<Entry<Class, AugmentationCodecWrapper>> codecs = new ArrayList<>(getImplementations().entrySet());
994 for (Entry<Class, AugmentationCodecWrapper> codec : codecs) {
995 AugmentationCodec<?> ac = codec.getValue();
996 if (ac.isAcceptable(path)) {
997 // We add Augmentation Identifier to path, in order to
998 // correctly identify children.
999 InstanceIdentifier augmentPath = path.builder().augmentation(codec.getKey()).build();
1000 ValueWithQName<?> value = codec.getValue().deserialize((CompositeNode) input, augmentPath);
1001 if (value != null && value.getValue() != null) {
1002 ret.put(codec.getKey(), (Augmentation) value.getValue());
1010 protected Optional<AugmentationCodecWrapper> tryToLoadImplementation(final Type potential) {
1012 Class<? extends Augmentation<?>> clazz = (Class<? extends Augmentation<?>>) classLoadingStrategy
1013 .loadClass(potential);
1014 return Optional.of(tryToLoadImplementation(clazz));
1015 } catch (ClassNotFoundException e) {
1016 LOG.warn("Failed to find class for augmentation of {}", potential, e);
1018 return Optional.absent();
1022 protected AugmentationCodecWrapper tryToLoadImplementation(final Class inputType) {
1023 AugmentationCodecWrapper<? extends Augmentation<?>> potentialImpl = getCodecForAugmentation(inputType);
1024 addImplementation(potentialImpl);
1025 return potentialImpl;
1029 protected void tryToLoadImplementations() {
1030 Type type = referencedType(augmentableType);
1031 Collection<Type> potentialAugmentations;
1032 synchronized (augmentableToAugmentations) {
1033 potentialAugmentations = new ArrayList(augmentableToAugmentations.get(type));
1035 for (Type potential : potentialAugmentations) {
1037 tryToLoadImplementation(potential);
1038 } catch (CodeGenerationException e) {
1039 LOG.warn("Failed to proactively generate augment code for {}", type, e);
1045 protected void adaptForPathImpl(final InstanceIdentifier<?> augTarget, final DataNodeContainer ctxNode) {
1046 if (ctxNode instanceof AugmentationTarget) {
1047 Set<AugmentationSchema> availableAugmentations = ((AugmentationTarget) ctxNode)
1048 .getAvailableAugmentations();
1049 if (!availableAugmentations.isEmpty()) {
1050 updateAugmentationMapping(augTarget, availableAugmentations);
1057 * Adapts augmentation codec for specific provider location (target)
1059 * Since augmentation are not forward-referencing and may be discovered
1060 * during runtime, we need to adapt {@link AugmentableDispatchCodec},
1061 * {@link AugmentationCodecWrapper} and {@link InstanceIdentifierCodec}
1062 * for this newly discovered location where augmentation may be used.
1064 * Adaptation consists of:
1066 * <li> scan of available (valid) augmentations for
1068 * <li>lookup for Java classes derived from this augmentations
1069 * <li>generation of missing codecs
1070 * <li>updating Augmentation codecs to work with new location
1071 * <li>updating Instance Identifier to work with new location
1074 private void updateAugmentationMapping(final InstanceIdentifier<?> augTarget,
1075 final Set<AugmentationSchema> availableAugmentations) {
1076 for (AugmentationSchema aug : availableAugmentations) {
1078 Type potentialType = getTypeForAugmentation(aug);
1079 if (potentialType != null) {
1080 Optional<AugmentationCodecWrapper> potentialImpl = tryToLoadImplementation(potentialType);
1081 if (potentialImpl.isPresent()) {
1082 potentialImpl.get().addApplicableFor(augTarget, aug);
1083 Class augType = potentialImpl.get().getDataType();
1084 InstanceIdentifier augPath = augTarget.augmentation(augType);
1087 org.opendaylight.yangtools.yang.data.api.InstanceIdentifier domPath = getInstanceIdentifierCodec().serialize(augPath);
1088 if(domPath == null) {
1089 LOG.error("Unable to serialize instance identifier for {}",augPath);
1091 } catch (Exception e) {
1092 LOG.error("Unable to serialize instance identifiers for {}",augPath,e);
1097 // Omits warning for empty augmentations since they are not represented in data
1098 if(!aug.getChildNodes().isEmpty()) {
1099 LOG.warn("Could not find generated type for augmentation {} with children {}", aug,
1100 aug.getChildNodes());
1108 private Type getTypeForAugmentation(final AugmentationSchema aug) {
1109 Optional<AugmentationSchema> currentAug = Optional.of(aug);
1110 while (currentAug.isPresent()) {
1111 Type potentialType = typeToAugment.inverse().get(currentAug.get());
1112 if (potentialType != null) {
1113 return potentialType;
1115 currentAug = currentAug.get().getOriginalDefinition();
1121 @SuppressWarnings("rawtypes")
1122 private static class AugmentationCodecWrapper<T extends Augmentation<?>> implements AugmentationCodec<T>,
1123 Delegator<BindingCodec>, LocationAwareBindingCodec<Node<?>, ValueWithQName<T>> {
1125 private final BindingCodec delegate;
1126 private final QName augmentationQName;
1127 private final Multimap<InstanceIdentifier<?>, QName> validAugmentationTargets;
1128 private final Class<?> augmentationType;
1130 public AugmentationCodecWrapper(final BindingCodec<Map<QName, Object>, Object> rawCodec, final Class<?> dataType) {
1131 this.delegate = rawCodec;
1132 this.augmentationType = dataType;
1133 this.augmentationQName = BindingReflections.findQName(rawCodec.getClass());
1134 this.validAugmentationTargets = Multimaps.synchronizedSetMultimap(HashMultimap
1135 .<InstanceIdentifier<?>, QName> create());
1138 public void addApplicableFor(final InstanceIdentifier<?> path, final AugmentationSchema aug) {
1139 for (DataSchemaNode child : aug.getChildNodes()) {
1140 validAugmentationTargets.put(path, child.getQName());
1145 public BindingCodec getDelegate() {
1150 public CompositeNode serialize(final ValueWithQName<T> input) {
1151 @SuppressWarnings("unchecked")
1152 List<Map<QName, Object>> rawValues = (List<Map<QName, Object>>) getDelegate().serialize(input);
1153 List<Node<?>> serialized = new ArrayList<>(rawValues.size());
1154 for (Map<QName, Object> val : rawValues) {
1155 serialized.add(IntermediateMapping.toNode(val));
1157 return new CompositeNodeTOImpl(input.getQname(), null, serialized);
1161 @SuppressWarnings("unchecked")
1162 public ValueWithQName<T> deserialize(final Node<?> input) {
1163 Object rawCodecValue = getDelegate().deserialize(input);
1164 return new ValueWithQName<T>(input.getNodeType(), (T) rawCodecValue);
1168 @SuppressWarnings("unchecked")
1169 public ValueWithQName<T> deserialize(final Node<?> input, final InstanceIdentifier<?> bindingIdentifier) {
1170 // if (!isAcceptable(bindingIdentifier)) {
1173 Object rawCodecValue = getDelegate().deserialize(input, bindingIdentifier);
1174 return new ValueWithQName<T>(input.getNodeType(), (T) rawCodecValue);
1178 public QName getAugmentationQName() {
1179 return augmentationQName;
1183 public boolean isAcceptable(final InstanceIdentifier<?> path) {
1187 return validAugmentationTargets.containsKey(path);
1191 public boolean isApplicable(final InstanceIdentifier location) {
1192 return isAcceptable(location);
1196 public Class<?> getDataType() {
1197 return augmentationType;
1201 @SuppressWarnings("rawtypes")
1202 private class IdentityCompositeCodec implements IdentityCodec {
1205 public Object deserialize(final Object input) {
1206 Preconditions.checkArgument(input instanceof QName);
1207 return deserialize((QName) input);
1211 public Class<?> deserialize(final QName input) {
1212 Type type = qnamesToIdentityMap.get(input);
1216 ReferencedTypeImpl typeref = new ReferencedTypeImpl(type.getPackageName(), type.getName());
1217 WeakReference<Class> softref = typeToClass.get(typeref);
1218 if (softref == null) {
1221 Class<?> cls = classLoadingStrategy.loadClass(typeref.getFullyQualifiedName());
1226 } catch (Exception e) {
1227 LOG.warn("Identity {} was not deserialized, because of missing class {}", input,
1228 typeref.getFullyQualifiedName());
1232 return softref.get();
1236 public Object deserialize(final Object input, final InstanceIdentifier bindingIdentifier) {
1237 Type type = qnamesToIdentityMap.get(input);
1241 ReferencedTypeImpl typeref = new ReferencedTypeImpl(type.getPackageName(), type.getName());
1242 WeakReference<Class> softref = typeToClass.get(typeref);
1243 if (softref == null) {
1246 Class<?> cls = classLoadingStrategy.loadClass(typeref.getFullyQualifiedName());
1251 } catch (Exception e) {
1252 LOG.warn("Identity {} was not deserialized, because of missing class {}", input,
1253 typeref.getFullyQualifiedName());
1257 return softref.get();
1261 public QName serialize(final Class input) {
1262 Preconditions.checkArgument(BaseIdentity.class.isAssignableFrom(input));
1263 bindingClassEncountered(input);
1264 QName qname = identityQNames.get(input);
1265 if (qname != null) {
1268 ConcreteType typeref = Types.typeForClass(input);
1269 qname = typeToQname.get(typeref);
1270 if (qname != null) {
1271 identityQNames.put(input, qname);
1277 public Object serialize(final Object input) {
1278 Preconditions.checkArgument(input instanceof Class);
1279 return serialize((Class) input);
1284 private static final Type referencedType(final Class<?> augmentableType) {
1285 return new ReferencedTypeImpl(augmentableType.getPackage().getName(), augmentableType.getSimpleName());