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;
14 import java.util.Map.Entry;
15 import java.util.concurrent.Callable;
16 import java.util.concurrent.ConcurrentHashMap;
17 import java.util.concurrent.ConcurrentMap;
19 import org.opendaylight.yangtools.sal.binding.generator.util.ClassLoaderUtils;
20 import org.opendaylight.yangtools.yang.data.impl.codec.*;
21 import org.opendaylight.yangtools.binding.generator.util.ReferencedTypeImpl;
22 import org.opendaylight.yangtools.binding.generator.util.Types;
23 import org.opendaylight.yangtools.concepts.Delegator;
24 import org.opendaylight.yangtools.concepts.Identifiable;
25 import org.opendaylight.yangtools.sal.binding.model.api.ConcreteType;
26 import org.opendaylight.yangtools.sal.binding.model.api.Type;
27 import org.opendaylight.yangtools.sal.binding.model.api.type.builder.GeneratedTOBuilder;
28 import org.opendaylight.yangtools.sal.binding.model.api.type.builder.GeneratedTypeBuilder;
29 import org.opendaylight.yangtools.yang.binding.*;
30 import org.opendaylight.yangtools.yang.binding.util.BindingReflections;
31 import org.opendaylight.yangtools.yang.common.QName;
32 import org.opendaylight.yangtools.yang.data.api.CompositeNode;
33 import org.opendaylight.yangtools.yang.data.api.Node;
34 import org.opendaylight.yangtools.yang.data.impl.CompositeNodeTOImpl;
35 import org.opendaylight.yangtools.yang.model.api.*;
36 import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
37 import org.slf4j.Logger;
38 import org.slf4j.LoggerFactory;
40 import com.google.common.base.Preconditions;
42 public class LazyGeneratedCodecRegistry implements //
44 SchemaServiceListener, //
47 private final static Logger LOG = LoggerFactory.getLogger(LazyGeneratedCodecRegistry.class);
48 private final static LateMixinCodec NOT_READY_CODEC = new LateMixinCodec();
50 private final InstanceIdentifierCodec instanceIdentifierCodec = new InstanceIdentifierCodecImpl(this);
51 private final IdentityCompositeCodec identityRefCodec = new IdentityCompositeCodec();
53 private TransformerGenerator generator;
55 // Concrete class to codecs
56 private static final Map<Class<?>, DataContainerCodec<?>> containerCodecs = Collections
57 .synchronizedMap(new WeakHashMap<Class<?>, DataContainerCodec<?>>());
58 private static final Map<Class<?>, IdentifierCodec<?>> identifierCodecs = Collections
59 .synchronizedMap(new WeakHashMap<Class<?>, IdentifierCodec<?>>());
60 private static final Map<Class<?>, ChoiceCodecImpl<?>> choiceCodecs = Collections
61 .synchronizedMap(new WeakHashMap<Class<?>, ChoiceCodecImpl<?>>());
62 private static final Map<Class<?>, ChoiceCaseCodecImpl<?>> caseCodecs = Collections
63 .synchronizedMap(new WeakHashMap<Class<?>, ChoiceCaseCodecImpl<?>>());
64 private static final Map<Class<?>, AugmentableCompositeCodec> augmentableCodecs = Collections
65 .synchronizedMap(new WeakHashMap<Class<?>, AugmentableCompositeCodec>());
66 private static final Map<Class<?>, AugmentationCodec<?>> augmentationCodecs = Collections
67 .synchronizedMap(new WeakHashMap<Class<?>, AugmentationCodec<?>>());
68 private static final Map<Class<?>, QName> identityQNames = Collections
69 .synchronizedMap(new WeakHashMap<Class<?>, QName>());
70 private static final Map<QName, Type> qnamesToIdentityMap = new ConcurrentHashMap<>();
71 /** Binding type to encountered classes mapping **/
72 @SuppressWarnings("rawtypes")
73 private static final Map<Type, WeakReference<Class>> typeToClass = new ConcurrentHashMap<>();
75 @SuppressWarnings("rawtypes")
76 private static final ConcurrentMap<Type, ChoiceCaseCodecImpl> typeToCaseCodecs = new ConcurrentHashMap<>();
78 private CaseClassMapFacade classToCaseRawCodec = new CaseClassMapFacade();
80 private static final Map<SchemaPath, GeneratedTypeBuilder> pathToType = new ConcurrentHashMap<>();
81 private static final Map<List<QName>, Type> pathToInstantiatedType = new ConcurrentHashMap<>();
82 private static final Map<Type, QName> typeToQname = new ConcurrentHashMap<>();
84 private final SchemaLock lock;
86 private SchemaContext currentSchema;
88 private GeneratedClassLoadingStrategy classLoadingStrategy = GeneratedClassLoadingStrategy.getTCCLClassLoadingStrategy();
90 LazyGeneratedCodecRegistry(SchemaLock lock) {
91 this.lock = Preconditions.checkNotNull(lock);
94 public SchemaLock getLock() {
98 public TransformerGenerator getGenerator() {
102 public void setGenerator(TransformerGenerator generator) {
103 this.generator = generator;
107 public InstanceIdentifierCodec getInstanceIdentifierCodec() {
108 return instanceIdentifierCodec;
112 public <T extends Augmentation<?>> AugmentationCodec<T> getCodecForAugmentation(Class<T> object) {
113 AugmentationCodec<T> codec = null;
114 @SuppressWarnings("rawtypes")
115 AugmentationCodec potentialCodec = augmentationCodecs.get(object);
116 if (potentialCodec != null) {
117 codec = potentialCodec;
120 lock.waitForSchema(object);
121 Class<? extends BindingCodec<Map<QName, Object>, Object>> augmentRawCodec = generator
122 .augmentationTransformerFor(object);
123 BindingCodec<Map<QName, Object>, Object> rawCodec = augmentRawCodec.newInstance();
124 codec = new AugmentationCodecWrapper<T>(rawCodec);
125 augmentationCodecs.put(augmentRawCodec, codec);
126 } catch (InstantiationException e) {
127 LOG.error("Can not instantiate raw augmentation codec {}", object.getSimpleName(), e);
128 } catch (IllegalAccessException e) {
130 "Run-time consistency issue: constructor {} is not available. This indicates either a code generation bug or a misconfiguration of JVM.",
131 object.getSimpleName(), e);
133 Class<? extends Augmentable<?>> objectSupertype = getAugmentableArgumentFrom(object);
134 if (objectSupertype != null) {
135 getAugmentableCodec(objectSupertype).addAugmentationCodec(object, codec);
137 LOG.warn("Could not find augmentation target for augmentation {}", object);
143 public QName getQNameForAugmentation(Class<?> cls) {
144 Preconditions.checkArgument(Augmentation.class.isAssignableFrom(cls));
145 return getCodecForAugmentation((Class<? extends Augmentation>) cls).getAugmentationQName();
148 private static Class<? extends Augmentable<?>> getAugmentableArgumentFrom(
149 final Class<? extends Augmentation<?>> augmentation) {
151 Class<? extends Augmentable<?>> ret = ClassLoaderUtils.withClassLoader(augmentation.getClassLoader(),
152 new Callable<Class<? extends Augmentable<?>>>() {
154 @SuppressWarnings("unchecked")
155 public Class<? extends Augmentable<?>> call() throws Exception {
156 for (java.lang.reflect.Type supertype : augmentation.getGenericInterfaces()) {
157 if (supertype instanceof ParameterizedType
158 && Augmentation.class.equals(((ParameterizedType) supertype).getRawType())) {
159 ParameterizedType augmentationGeneric = (ParameterizedType) supertype;
160 return (Class<? extends Augmentable<?>>) augmentationGeneric
161 .getActualTypeArguments()[0];
168 } catch (Exception e) {
169 LOG.debug("Could not find augmentable for {} using {}", augmentation, augmentation.getClassLoader(), e);
175 public Class<?> getClassForPath(List<QName> names) {
176 DataSchemaNode node = getSchemaNode(names);
177 SchemaPath path = node.getPath();
178 Type type = pathToType.get(path);
180 type = new ReferencedTypeImpl(type.getPackageName(), type.getName());
182 type = pathToInstantiatedType.get(names);
184 @SuppressWarnings("rawtypes")
185 WeakReference<Class> weakRef = typeToClass.get(type);
186 if (weakRef == null) {
187 LOG.error("Could not find loaded class for path: {} and type: {}", path, type.getFullyQualifiedName());
189 return weakRef.get();
193 public void putPathToClass(List<QName> names, Class<?> cls) {
194 Type reference = Types.typeForClass(cls);
195 pathToInstantiatedType.put(names, reference);
196 bindingClassEncountered(cls);
200 public IdentifierCodec<?> getKeyCodecForPath(List<QName> names) {
201 @SuppressWarnings("unchecked")
202 Class<? extends Identifiable<?>> cls = (Class<? extends Identifiable<?>>) getClassForPath(names);
203 return getIdentifierCodecForIdentifiable(cls);
207 public <T extends DataContainer> DataContainerCodec<T> getCodecForDataObject(Class<T> type) {
208 @SuppressWarnings("unchecked")
209 DataContainerCodec<T> ret = (DataContainerCodec<T>) containerCodecs.get(type);
213 Class<? extends BindingCodec<Map<QName, Object>, Object>> newType = generator.transformerFor(type);
214 BindingCodec<Map<QName, Object>, Object> rawCodec = newInstanceOf(newType);
215 DataContainerCodecImpl<T> newWrapper = new DataContainerCodecImpl<>(rawCodec);
216 containerCodecs.put(type, newWrapper);
220 @SuppressWarnings("rawtypes")
221 public void bindingClassEncountered(Class cls) {
223 ConcreteType typeRef = Types.typeForClass(cls);
224 if (typeToClass.containsKey(typeRef)) {
227 LOG.trace("Binding Class {} encountered.", cls);
228 WeakReference<Class> weakRef = new WeakReference<>(cls);
229 typeToClass.put(typeRef, weakRef);
230 if (Augmentation.class.isAssignableFrom(cls)) {
232 } else if (DataObject.class.isAssignableFrom(cls)) {
233 @SuppressWarnings({ "unchecked", "unused" })
234 Object cdc = getCodecForDataObject((Class<? extends DataObject>) cls);
239 public void onClassProcessed(Class<?> cls) {
240 ConcreteType typeRef = Types.typeForClass(cls);
241 if (typeToClass.containsKey(typeRef)) {
244 LOG.trace("Binding Class {} encountered.", cls);
245 WeakReference<Class> weakRef = new WeakReference<>((Class) cls);
246 typeToClass.put(typeRef, weakRef);
249 private DataSchemaNode getSchemaNode(List<QName> path) {
250 QName firstNode = path.get(0);
251 DataNodeContainer previous = currentSchema.findModuleByNamespaceAndRevision(firstNode.getNamespace(),
252 firstNode.getRevision());
253 Iterator<QName> iterator = path.iterator();
254 while (iterator.hasNext()) {
255 QName arg = iterator.next();
256 DataSchemaNode currentNode = previous.getDataChildByName(arg);
257 if (currentNode == null && previous instanceof DataNodeContainer) {
258 currentNode = searchInChoices(previous, arg);
260 if (currentNode instanceof DataNodeContainer) {
261 previous = (DataNodeContainer) currentNode;
262 } else if (currentNode instanceof LeafSchemaNode || currentNode instanceof LeafListSchemaNode) {
263 Preconditions.checkState(!iterator.hasNext(), "Path tries to nest inside leaf node.");
267 return (DataSchemaNode) previous;
270 private DataSchemaNode searchInChoices(DataNodeContainer node, QName arg) {
271 Set<DataSchemaNode> children = node.getChildNodes();
272 for (DataSchemaNode child : children) {
273 if (child instanceof ChoiceNode) {
274 ChoiceNode choiceNode = (ChoiceNode) child;
275 DataSchemaNode potential = searchInCases(choiceNode, arg);
276 if (potential != null) {
284 private DataSchemaNode searchInCases(ChoiceNode choiceNode, QName arg) {
285 Set<ChoiceCaseNode> cases = choiceNode.getCases();
286 for (ChoiceCaseNode caseNode : cases) {
287 DataSchemaNode node = caseNode.getDataChildByName(arg);
295 private <T> T newInstanceOf(Class<?> newType) {
297 @SuppressWarnings("unchecked")
298 T ret = (T) newType.newInstance();
300 } catch (InstantiationException e) {
301 throw new IllegalStateException(e);
302 } catch (IllegalAccessException e) {
303 throw new IllegalStateException(e);
308 public <T extends Identifiable<?>> IdentifierCodec<?> getIdentifierCodecForIdentifiable(Class<T> type) {
309 IdentifierCodec<?> obj = identifierCodecs.get(type);
313 Class<? extends BindingCodec<Map<QName, Object>, Object>> newCodec = generator
314 .keyTransformerForIdentifiable(type);
315 BindingCodec<Map<QName, Object>, Object> newInstance;
316 newInstance = newInstanceOf(newCodec);
317 IdentifierCodecImpl<?> newWrapper = new IdentifierCodecImpl<>(newInstance);
318 identifierCodecs.put(type, newWrapper);
323 public IdentityCodec<?> getIdentityCodec() {
324 return identityRefCodec;
328 public <T extends BaseIdentity> IdentityCodec<T> getCodecForIdentity(Class<T> codec) {
329 bindingClassEncountered(codec);
330 return identityRefCodec;
334 public void onCodecCreated(Class<?> cls) {
335 CodecMapping.setIdentifierCodec(cls, instanceIdentifierCodec);
336 CodecMapping.setIdentityRefCodec(cls, identityRefCodec);
340 public <T extends Identifier<?>> IdentifierCodec<T> getCodecForIdentifier(Class<T> object) {
341 @SuppressWarnings("unchecked")
342 IdentifierCodec<T> obj = (IdentifierCodec<T>) identifierCodecs.get(object);
346 Class<? extends BindingCodec<Map<QName, Object>, Object>> newCodec = generator
347 .keyTransformerForIdentifier(object);
348 BindingCodec<Map<QName, Object>, Object> newInstance;
349 newInstance = newInstanceOf(newCodec);
350 IdentifierCodecImpl<T> newWrapper = new IdentifierCodecImpl<>(newInstance);
351 identifierCodecs.put(object, newWrapper);
355 @SuppressWarnings("rawtypes")
356 public ChoiceCaseCodecImpl getCaseCodecFor(Class caseClass) {
357 ChoiceCaseCodecImpl<?> potential = caseCodecs.get(caseClass);
358 if (potential != null) {
361 ConcreteType typeref = Types.typeForClass(caseClass);
362 ChoiceCaseCodecImpl caseCodec = typeToCaseCodecs.get(typeref);
364 Preconditions.checkState(caseCodec != null, "Case Codec was not created proactivelly for %s",
365 caseClass.getName());
366 Preconditions.checkState(caseCodec.getSchema() != null, "Case schema is not available for %s",
367 caseClass.getName());
368 @SuppressWarnings("unchecked")
369 Class<? extends BindingCodec> newCodec = generator.caseCodecFor(caseClass, caseCodec.getSchema());
370 BindingCodec newInstance = newInstanceOf(newCodec);
371 caseCodec.setDelegate(newInstance);
372 caseCodecs.put(caseClass, caseCodec);
374 for (Entry<Class<?>, ChoiceCodecImpl<?>> choice : choiceCodecs.entrySet()) {
375 if (choice.getKey().isAssignableFrom(caseClass)) {
376 choice.getValue().cases.put(caseClass, caseCodec);
382 public void onModuleContextAdded(SchemaContext schemaContext, Module module, ModuleContext context) {
383 pathToType.putAll(context.getChildNodes());
384 qnamesToIdentityMap.putAll(context.getIdentities());
385 for (Entry<QName, GeneratedTOBuilder> identity : context.getIdentities().entrySet()) {
387 new ReferencedTypeImpl(identity.getValue().getPackageName(), identity.getValue().getName()),
390 captureCases(context.getCases(), schemaContext);
393 private void captureCases(Map<SchemaPath, GeneratedTypeBuilder> cases, SchemaContext module) {
394 for (Entry<SchemaPath, GeneratedTypeBuilder> caseNode : cases.entrySet()) {
395 ReferencedTypeImpl typeref = new ReferencedTypeImpl(caseNode.getValue().getPackageName(), caseNode
396 .getValue().getName());
398 pathToType.put(caseNode.getKey(), caseNode.getValue());
400 ChoiceCaseNode node = (ChoiceCaseNode) SchemaContextUtil.findDataSchemaNode(module, caseNode.getKey());
403 LOG.warn("Failed to find YANG SchemaNode for {}, with path {} was not found in context.",
404 typeref.getFullyQualifiedName(), caseNode.getKey());
405 @SuppressWarnings("rawtypes")
406 ChoiceCaseCodecImpl value = new ChoiceCaseCodecImpl();
407 typeToCaseCodecs.putIfAbsent(typeref, value);
410 @SuppressWarnings("rawtypes")
411 ChoiceCaseCodecImpl value = new ChoiceCaseCodecImpl(node);
412 typeToCaseCodecs.putIfAbsent(typeref, value);
417 public void onGlobalContextUpdated(SchemaContext context) {
418 currentSchema = context;
421 @SuppressWarnings({ "unchecked", "rawtypes" })
423 public void onChoiceCodecCreated(Class<?> choiceClass,
424 Class<? extends BindingCodec<Map<QName, Object>, Object>> choiceCodec, ChoiceNode schema) {
425 ChoiceCodec<?> oldCodec = choiceCodecs.get(choiceClass);
426 Preconditions.checkState(oldCodec == null);
427 BindingCodec<Map<QName, Object>, Object> delegate = newInstanceOf(choiceCodec);
428 ChoiceCodecImpl<?> newCodec = new ChoiceCodecImpl(delegate);
429 choiceCodecs.put(choiceClass, newCodec);
430 CodecMapping.setClassToCaseMap(choiceCodec, (Map<Class<?>, BindingCodec<?, ?>>) classToCaseRawCodec);
431 CodecMapping.setCompositeNodeToCaseMap(choiceCodec, newCodec.getCompositeToCase());
433 tryToCreateCasesCodecs(schema);
437 private void tryToCreateCasesCodecs(ChoiceNode schema) {
438 for (ChoiceCaseNode caseNode : schema.getCases()) {
439 SchemaPath path = caseNode.getPath();
440 GeneratedTypeBuilder type;
441 if (path != null && (type = pathToType.get(path)) != null) {
442 ReferencedTypeImpl typeref = new ReferencedTypeImpl(type.getPackageName(), type.getName());
443 ChoiceCaseCodecImpl partialCodec = typeToCaseCodecs.get(typeref);
444 if (partialCodec.getSchema() == null) {
445 partialCodec.setSchema(caseNode);
448 Class<?> caseClass = classLoadingStrategy.loadClass(type.getFullyQualifiedName());
449 getCaseCodecFor(caseClass);
450 } catch (ClassNotFoundException e) {
451 LOG.trace("Could not proactivelly create case codec for {}", type, e);
459 public void onValueCodecCreated(Class<?> valueClass, Class<?> valueCodec) {
463 public void onCaseCodecCreated(Class<?> choiceClass,
464 Class<? extends BindingCodec<Map<QName, Object>, Object>> choiceCodec) {
468 public void onDataContainerCodecCreated(Class<?> dataClass, Class<? extends BindingCodec<?, ?>> dataCodec) {
469 if (Augmentable.class.isAssignableFrom(dataClass)) {
470 AugmentableCompositeCodec augmentableCodec = getAugmentableCodec(dataClass);
471 CodecMapping.setAugmentationCodec(dataCodec, augmentableCodec);
476 public AugmentableCompositeCodec getAugmentableCodec(Class<?> dataClass) {
477 AugmentableCompositeCodec ret = augmentableCodecs.get(dataClass);
481 ret = new AugmentableCompositeCodec(dataClass);
482 augmentableCodecs.put(dataClass, ret);
486 private static abstract class IntermediateCodec<T> implements //
487 DomCodec<T>, Delegator<BindingCodec<Map<QName, Object>, Object>> {
489 private final BindingCodec<Map<QName, Object>, Object> delegate;
492 public BindingCodec<Map<QName, Object>, Object> getDelegate() {
496 public IntermediateCodec(BindingCodec<Map<QName, Object>, Object> delegate) {
497 this.delegate = delegate;
501 public Node<?> serialize(ValueWithQName<T> input) {
502 Map<QName, Object> intermediateOutput = delegate.serialize(input);
503 return IntermediateMapping.toNode(intermediateOutput);
507 private static class IdentifierCodecImpl<T extends Identifier<?>> //
508 extends IntermediateCodec<T> //
509 implements IdentifierCodec<T> {
511 public IdentifierCodecImpl(BindingCodec<Map<QName, Object>, Object> delegate) {
516 public ValueWithQName<T> deserialize(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 CompositeNode serialize(ValueWithQName<T> input) {
525 return (CompositeNode) super.serialize(input);
529 private static class DataContainerCodecImpl<T extends DataContainer> //
530 extends IntermediateCodec<T> //
531 implements DataContainerCodec<T> {
533 public DataContainerCodecImpl(BindingCodec<Map<QName, Object>, Object> delegate) {
538 public ValueWithQName<T> deserialize(Node<?> input) {
542 QName qname = input.getNodeType();
543 @SuppressWarnings("unchecked")
544 T value = (T) getDelegate().deserialize((Map<QName, Object>) input);
545 return new ValueWithQName<T>(qname, value);
549 public CompositeNode serialize(ValueWithQName<T> input) {
550 return (CompositeNode) super.serialize(input);
554 @SuppressWarnings("rawtypes")
555 private static class ChoiceCaseCodecImpl<T extends DataContainer> implements ChoiceCaseCodec<T>, //
556 Delegator<BindingCodec> {
557 private boolean augmenting;
558 private BindingCodec delegate;
560 private Set<String> validNames;
561 private Set<QName> validQNames;
562 private ChoiceCaseNode schema;
564 public void setSchema(ChoiceCaseNode caseNode) {
565 this.schema = schema;
566 this.schema = caseNode;
567 validNames = new HashSet<>();
568 validQNames = new HashSet<>();
569 for (DataSchemaNode node : caseNode.getChildNodes()) {
570 QName qname = node.getQName();
571 validQNames.add(qname);
572 validNames.add(qname.getLocalName());
574 augmenting = caseNode.isAugmenting();
577 public ChoiceCaseCodecImpl() {
578 this.delegate = NOT_READY_CODEC;
581 public ChoiceCaseCodecImpl(ChoiceCaseNode caseNode) {
582 this.delegate = NOT_READY_CODEC;
587 public ValueWithQName<T> deserialize(Node<?> input) {
588 throw new UnsupportedOperationException("Direct invocation of this codec is not allowed.");
592 public CompositeNode serialize(ValueWithQName<T> input) {
593 throw new UnsupportedOperationException("Direct invocation of this codec is not allowed.");
596 public BindingCodec getDelegate() {
600 public void setDelegate(BindingCodec delegate) {
601 this.delegate = delegate;
604 public ChoiceCaseNode getSchema() {
609 public boolean isAcceptable(Node<?> input) {
610 if (input instanceof CompositeNode) {
612 return checkAugmenting((CompositeNode) input);
614 return checkLocal((CompositeNode) input);
620 private boolean checkLocal(CompositeNode input) {
621 QName parent = input.getNodeType();
622 for (Node<?> childNode : input.getChildren()) {
623 QName child = childNode.getNodeType();
624 if (!Objects.equals(parent.getNamespace(), child.getNamespace())
625 || !Objects.equals(parent.getRevision(), child.getRevision())) {
628 if (validNames.contains(child.getLocalName())) {
635 private boolean checkAugmenting(CompositeNode input) {
636 for (Node<?> child : input.getChildren()) {
637 if (validQNames.contains(child.getNodeType())) {
645 private static class ChoiceCodecImpl<T> implements ChoiceCodec<T> {
647 private final BindingCodec<Map<QName, Object>, Object> delegate;
649 @SuppressWarnings("rawtypes")
650 private final Map<Class, ChoiceCaseCodecImpl<?>> cases = Collections
651 .synchronizedMap(new WeakHashMap<Class, ChoiceCaseCodecImpl<?>>());
653 private final CaseCompositeNodeMapFacade CompositeToCase;
655 public ChoiceCodecImpl(BindingCodec<Map<QName, Object>, Object> delegate) {
656 this.delegate = delegate;
657 this.CompositeToCase = new CaseCompositeNodeMapFacade(cases);
661 public ValueWithQName<T> deserialize(Node<?> input) {
662 throw new UnsupportedOperationException("Direct invocation of this codec is not allowed.");
666 public Node<?> serialize(ValueWithQName<T> input) {
667 throw new UnsupportedOperationException("Direct invocation of this codec is not allowed.");
670 public CaseCompositeNodeMapFacade getCompositeToCase() {
671 return CompositeToCase;
674 public Map<Class, ChoiceCaseCodecImpl<?>> getCases() {
678 public BindingCodec<Map<QName, Object>, Object> getDelegate() {
684 @SuppressWarnings("rawtypes")
685 private class CaseClassMapFacade extends MapFacadeBase {
688 public Set<Entry<Class, BindingCodec<Object, Object>>> entrySet() {
689 return Collections.emptySet();
693 public BindingCodec get(Object key) {
694 if (key instanceof Class) {
695 Class cls = (Class) key;
696 // bindingClassEncountered(cls);
697 ChoiceCaseCodecImpl caseCodec = getCaseCodecFor(cls);
698 return caseCodec.getDelegate();
704 @SuppressWarnings("rawtypes")
705 private static class CaseCompositeNodeMapFacade extends MapFacadeBase<CompositeNode> {
707 final Map<Class, ChoiceCaseCodecImpl<?>> choiceCases;
709 public CaseCompositeNodeMapFacade(Map<Class, ChoiceCaseCodecImpl<?>> choiceCases) {
710 this.choiceCases = choiceCases;
714 public BindingCodec get(Object key) {
715 if (!(key instanceof CompositeNode)) {
718 for (Entry<Class, ChoiceCaseCodecImpl<?>> entry : choiceCases.entrySet()) {
719 ChoiceCaseCodecImpl<?> codec = entry.getValue();
720 if (codec.isAcceptable((CompositeNode) key)) {
721 return codec.getDelegate();
730 * This map is used as only facade for
731 * {@link org.opendaylight.yangtools.yang.binding.BindingCodec} in different
732 * classloaders to retrieve codec dynamicly based on provided key.
737 @SuppressWarnings("rawtypes")
738 private static abstract class MapFacadeBase<T> implements Map<T, BindingCodec<?, ?>> {
741 public boolean containsKey(Object key) {
742 return get(key) != null;
746 public void clear() {
747 throw notModifiable();
751 public boolean equals(Object obj) {
752 return super.equals(obj);
756 public BindingCodec remove(Object key) {
766 public Collection<BindingCodec<?, ?>> values() {
767 return Collections.emptySet();
770 private UnsupportedOperationException notModifiable() {
771 return new UnsupportedOperationException("Not externally modifiable.");
775 public BindingCodec<Map<QName, Object>, Object> put(T key, BindingCodec<?, ?> value) {
776 throw notModifiable();
780 public void putAll(Map<? extends T, ? extends BindingCodec<?, ?>> m) {
781 throw notModifiable();
785 public int hashCode() {
786 return super.hashCode();
790 public boolean isEmpty() {
795 public Set<T> keySet() {
796 return Collections.emptySet();
800 public Set<Entry<T, BindingCodec<?, ?>>> entrySet() {
801 return Collections.emptySet();
805 public boolean containsValue(Object value) {
810 @SuppressWarnings({ "rawtypes", "unchecked" })
811 private class AugmentableCompositeCodec implements BindingCodec {
813 private final Class augmentableType;
815 Map<Class, AugmentationCodec<?>> localAugmentationCodecs = Collections
816 .synchronizedMap(new WeakHashMap<Class, AugmentationCodec<?>>());
818 public AugmentableCompositeCodec(Class type) {
819 Preconditions.checkArgument(Augmentable.class.isAssignableFrom(type));
820 augmentableType = type;
824 public Object serialize(Object input) {
825 if (input instanceof Augmentable<?>) {
827 Map<Class, Augmentation> augmentations = getAugmentations(input);
828 return serializeImpl(augmentations);
833 private Map<Class, Augmentation> getAugmentations(Object input) {
834 Field augmentationField;
836 augmentationField = input.getClass().getDeclaredField("augmentation");
837 augmentationField.setAccessible(true);
838 Map<Class, Augmentation> augMap = (Map<Class, Augmentation>) augmentationField.get(input);
840 } catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException e) {
841 LOG.debug("Could not read augmentations for {}", input, e);
843 return Collections.emptyMap();
846 private List serializeImpl(Map<Class, Augmentation> input) {
847 List ret = new ArrayList<>();
848 for (Entry<Class, Augmentation> entry : input.entrySet()) {
849 AugmentationCodec codec = getCodecForAugmentation(entry.getKey());
850 CompositeNode node = codec.serialize(new ValueWithQName(null, entry.getValue()));
851 ret.addAll(node.getChildren());
856 public synchronized <T extends Augmentation<?>> void addAugmentationCodec(Class<T> augmentationClass,
857 AugmentationCodec<T> value) {
858 localAugmentationCodecs.put(augmentationClass, value);
862 public Map<Class, Augmentation> deserialize(Object input) {
863 Map<Class, Augmentation> ret = new HashMap<>();
864 if (input instanceof CompositeNode) {
865 List<Entry<Class, AugmentationCodec<?>>> codecs = new ArrayList<>(localAugmentationCodecs.entrySet());
866 for (Entry<Class, AugmentationCodec<?>> codec : codecs) {
867 ValueWithQName<?> value = codec.getValue().deserialize((CompositeNode) input);
868 if (value != null && value.getValue() != null) {
869 ret.put(codec.getKey(), (Augmentation) value.getValue());
876 public Class getAugmentableType() {
877 return augmentableType;
881 @SuppressWarnings({ "rawtypes", "unchecked" })
882 private static class LateMixinCodec implements BindingCodec, Delegator<BindingCodec> {
884 private BindingCodec delegate;
887 public BindingCodec getDelegate() {
888 if (delegate == null) {
889 throw new IllegalStateException("Codec not initialized yet.");
895 public Object deserialize(Object input) {
896 return getDelegate().deserialize(input);
900 public Object serialize(Object input) {
901 return getDelegate().serialize(input);
905 private static class AugmentationCodecWrapper<T extends Augmentation<?>> implements AugmentationCodec<T>,
906 Delegator<BindingCodec> {
908 private BindingCodec delegate;
909 private QName augmentationQName;
911 public AugmentationCodecWrapper(BindingCodec<Map<QName, Object>, Object> rawCodec) {
912 this.delegate = rawCodec;
913 this.augmentationQName = BindingReflections.findQName(rawCodec.getClass());
917 public BindingCodec getDelegate() {
922 public CompositeNode serialize(ValueWithQName<T> input) {
923 @SuppressWarnings("unchecked")
924 List<Map<QName, Object>> rawValues = (List<Map<QName, Object>>) getDelegate().serialize(input);
925 List<Node<?>> serialized = new ArrayList<>(rawValues.size());
926 for (Map<QName, Object> val : rawValues) {
927 serialized.add(IntermediateMapping.toNode(val));
929 return new CompositeNodeTOImpl(input.getQname(), null, serialized);
933 @SuppressWarnings("unchecked")
934 public ValueWithQName<T> deserialize(Node<?> input) {
935 Object rawCodecValue = getDelegate().deserialize((Map<QName, Object>) input);
936 return new ValueWithQName<T>(input.getNodeType(), (T) rawCodecValue);
940 public QName getAugmentationQName() {
941 return augmentationQName;
945 private class IdentityCompositeCodec implements IdentityCodec {
948 public Object deserialize(Object input) {
949 Preconditions.checkArgument(input instanceof QName);
950 return deserialize((QName) input);
954 public Class<?> deserialize(QName input) {
955 Type type = qnamesToIdentityMap.get(input);
959 ReferencedTypeImpl typeref = new ReferencedTypeImpl(type.getPackageName(), type.getName());
960 WeakReference<Class> softref = typeToClass.get(typeref);
961 if (softref == null) {
964 Class<?> cls = classLoadingStrategy.loadClass(typeref.getFullyQualifiedName());
969 } catch (Exception e) {
970 LOG.warn("Identity {} was not deserialized, because of missing class {}", input,
971 typeref.getFullyQualifiedName());
975 return softref.get();
979 public QName serialize(Class input) {
980 Preconditions.checkArgument(BaseIdentity.class.isAssignableFrom(input));
981 bindingClassEncountered(input);
982 QName qname = identityQNames.get(input);
986 ConcreteType typeref = Types.typeForClass(input);
987 qname = typeToQname.get(typeref);
989 identityQNames.put(input, qname);
995 public Object serialize(Object input) {
996 Preconditions.checkArgument(input instanceof Class);
997 return serialize((Class) input);
1001 public boolean isCodecAvailable(Class<? extends DataContainer> cls) {
1002 if (containerCodecs.containsKey(cls)) {
1005 if (identifierCodecs.containsKey(cls)) {
1008 if (choiceCodecs.containsKey(cls)) {
1011 if (caseCodecs.containsKey(cls)) {
1014 if (augmentableCodecs.containsKey(cls)) {
1017 if (augmentationCodecs.containsKey(cls)) {