X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=code-generator%2Fbinding-generator-impl%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fyangtools%2Fsal%2Fbinding%2Fgenerator%2Fimpl%2FLazyGeneratedCodecRegistry.java;h=c251ba18203add8324274bf6a6a8e63563eb263e;hb=d56ef2d088c99b7dbf7964ac49fb18b4659272f8;hp=a2c1e4ce88f590e87cafe2bfc74448672bfc0a73;hpb=1a82cbe4c8d80b5e5a1721ebb4e8f8a1f33f1d76;p=mdsal.git diff --git a/code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/LazyGeneratedCodecRegistry.java b/code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/LazyGeneratedCodecRegistry.java index a2c1e4ce88..c251ba1820 100644 --- a/code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/LazyGeneratedCodecRegistry.java +++ b/code-generator/binding-generator-impl/src/main/java/org/opendaylight/yangtools/sal/binding/generator/impl/LazyGeneratedCodecRegistry.java @@ -9,6 +9,7 @@ package org.opendaylight.yangtools.sal.binding.generator.impl; import java.lang.ref.WeakReference; import java.lang.reflect.Field; +import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -18,7 +19,6 @@ import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; -import java.util.Objects; import java.util.Set; import java.util.WeakHashMap; import java.util.concurrent.ConcurrentHashMap; @@ -33,7 +33,6 @@ import org.opendaylight.yangtools.sal.binding.generator.util.CodeGenerationExcep import org.opendaylight.yangtools.sal.binding.model.api.ConcreteType; import org.opendaylight.yangtools.sal.binding.model.api.Type; import org.opendaylight.yangtools.sal.binding.model.api.type.builder.GeneratedTOBuilder; -import org.opendaylight.yangtools.sal.binding.model.api.type.builder.GeneratedTypeBuilder; import org.opendaylight.yangtools.yang.binding.Augmentable; import org.opendaylight.yangtools.yang.binding.Augmentation; import org.opendaylight.yangtools.yang.binding.BaseIdentity; @@ -69,7 +68,6 @@ import org.opendaylight.yangtools.yang.model.api.Module; import org.opendaylight.yangtools.yang.model.api.SchemaContext; import org.opendaylight.yangtools.yang.model.api.SchemaContextListener; import org.opendaylight.yangtools.yang.model.api.SchemaPath; -import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -78,6 +76,7 @@ import com.google.common.base.Preconditions; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; import com.google.common.collect.HashMultimap; +import com.google.common.collect.Iterables; import com.google.common.collect.Multimap; import com.google.common.collect.Multimaps; @@ -87,7 +86,6 @@ class LazyGeneratedCodecRegistry implements // GeneratorListener { private static final Logger LOG = LoggerFactory.getLogger(LazyGeneratedCodecRegistry.class); - private static final LateMixinCodec NOT_READY_CODEC = new LateMixinCodec(); // Concrete class to codecs private static final Map, DataContainerCodec> containerCodecs = Collections @@ -109,10 +107,9 @@ class LazyGeneratedCodecRegistry implements // @SuppressWarnings("rawtypes") private static final Map> typeToClass = new ConcurrentHashMap<>(); - @SuppressWarnings("rawtypes") - private static final ConcurrentMap typeToCaseCodecs = new ConcurrentHashMap<>(); + private static final ConcurrentMap caseTypeToCaseSchema = new ConcurrentHashMap<>(); - private static final Map pathToType = new ConcurrentHashMap<>(); + private static final Map pathToType = new ConcurrentHashMap<>(); private static final Map, Type> pathToInstantiatedType = new ConcurrentHashMap<>(); private static final Map typeToQname = new ConcurrentHashMap<>(); private static final BiMap typeToAugment = HashBiMap @@ -124,7 +121,6 @@ class LazyGeneratedCodecRegistry implements // . create()); private final InstanceIdentifierCodec instanceIdentifierCodec = new InstanceIdentifierCodecImpl(this); - private final CaseClassMapFacade classToCaseRawCodec = new CaseClassMapFacade(); private final IdentityCompositeCodec identityRefCodec = new IdentityCompositeCodec(); private final ClassLoadingStrategy classLoadingStrategy; private final AbstractTransformerGenerator generator; @@ -151,32 +147,32 @@ class LazyGeneratedCodecRegistry implements // @SuppressWarnings("unchecked") @Override - public > AugmentationCodecWrapper getCodecForAugmentation(final Class object) { + public > AugmentationCodecWrapper getCodecForAugmentation(final Class augClass) { AugmentationCodecWrapper codec = null; @SuppressWarnings("rawtypes") - AugmentationCodecWrapper potentialCodec = augmentationCodecs.get(object); + AugmentationCodecWrapper potentialCodec = augmentationCodecs.get(augClass); if (potentialCodec != null) { codec = potentialCodec; } else { - lock.waitForSchema(object); + lock.waitForSchema(augClass); Class, Object>> augmentRawCodec = generator - .augmentationTransformerFor(object); + .augmentationTransformerFor(augClass); BindingCodec, Object> rawCodec = newInstanceOf(augmentRawCodec); - codec = new AugmentationCodecWrapper(rawCodec, null, object); - augmentationCodecs.put(object, codec); + codec = new AugmentationCodecWrapper(rawCodec, augClass); + augmentationCodecs.put(augClass, codec); } final Class> objectSupertype; try { - objectSupertype = BindingReflections.findAugmentationTarget(object); + objectSupertype = BindingReflections.findAugmentationTarget(augClass); } catch (Exception e) { - LOG.warn("Failed to find target for augmentation {}, ignoring it", object, e); + LOG.warn("Failed to find target for augmentation {}, ignoring it", augClass, e); return codec; } if (objectSupertype == null) { - LOG.warn("Augmentation target for {} not found, ignoring it", object); + LOG.warn("Augmentation target for {} not found, ignoring it", augClass); return codec; } @@ -207,7 +203,8 @@ class LazyGeneratedCodecRegistry implements // @SuppressWarnings("rawtypes") final WeakReference weakRef = typeToClass.get(type); - Preconditions.checkState(weakRef != null, "Could not find loaded class for path: %s and type: %s", path, type.getFullyQualifiedName()); + Preconditions.checkState(weakRef != null, "Could not find loaded class for path: %s and type: %s", path, + type.getFullyQualifiedName()); return weakRef.get(); } @@ -273,7 +270,8 @@ class LazyGeneratedCodecRegistry implements // private DataSchemaNode getSchemaNode(final List path) { QName firstNode = path.get(0); - DataNodeContainer previous = currentSchema.findModuleByNamespaceAndRevision(firstNode.getNamespace(), firstNode.getRevision()); + DataNodeContainer previous = currentSchema.findModuleByNamespaceAndRevision(firstNode.getNamespace(), + firstNode.getRevision()); Preconditions.checkArgument(previous != null, "Failed to find module %s for path %s", firstNode, path); Iterator iterator = path.iterator(); @@ -327,7 +325,8 @@ class LazyGeneratedCodecRegistry implements // LOG.error("Failed to instantiate codec {}", cls.getSimpleName(), e); throw new IllegalStateException(String.format("Failed to instantiate codec %s", cls), e); } catch (IllegalAccessException e) { - LOG.debug("Run-time consistency issue: constructor for {} is not available. This indicates either a code generation bug or a misconfiguration of JVM.", + LOG.debug( + "Run-time consistency issue: constructor for {} is not available. This indicates either a code generation bug or a misconfiguration of JVM.", cls.getSimpleName(), e); throw new IllegalStateException(String.format("Cannot access contructor of %s", cls), e); } @@ -389,15 +388,12 @@ class LazyGeneratedCodecRegistry implements // return potential; } ConcreteType typeref = Types.typeForClass(caseClass); - ChoiceCaseCodecImpl caseCodec = typeToCaseCodecs.get(typeref); + ChoiceCaseNode caseSchema = caseTypeToCaseSchema.get(typeref); - Preconditions.checkState(caseCodec != null, "Case Codec was not created proactivelly for %s", - caseClass.getName()); - Preconditions.checkState(caseCodec.getSchema() != null, "Case schema is not available for %s", - caseClass.getName()); - Class newCodec = generator.caseCodecFor(caseClass, caseCodec.getSchema()); + Preconditions.checkState(caseSchema != null, "Case schema is not available for %s", caseClass.getName()); + Class newCodec = generator.caseCodecFor(caseClass, caseSchema); BindingCodec newInstance = newInstanceOf(newCodec); - caseCodec.setDelegate(newInstance); + ChoiceCaseCodecImpl caseCodec = new ChoiceCaseCodecImpl(caseClass, caseSchema, newInstance); caseCodecs.put(caseClass, caseCodec); for (Entry, PublicChoiceCodecImpl> choice : choiceCodecs.entrySet()) { @@ -428,35 +424,14 @@ class LazyGeneratedCodecRegistry implements // identity.getKey()); } - synchronized(augmentableToAugmentations) { + synchronized (augmentableToAugmentations) { augmentableToAugmentations.putAll(context.getAugmentableToAugmentations()); } - synchronized(choiceToCases) { + synchronized (choiceToCases) { choiceToCases.putAll(context.getChoiceToCases()); } - captureCases(context.getCases(), schemaContext); - } - - private void captureCases(final Map cases, final SchemaContext module) { - for (Entry caseNode : cases.entrySet()) { - ReferencedTypeImpl typeref = new ReferencedTypeImpl(caseNode.getValue().getPackageName(), caseNode - .getValue().getName()); - - pathToType.put(caseNode.getKey(), caseNode.getValue()); - - ChoiceCaseNode node = (ChoiceCaseNode) SchemaContextUtil.findDataSchemaNode(module, caseNode.getKey()); - - if (node == null) { - LOG.warn("Failed to find YANG SchemaNode for {}, with path {} was not found in context.", - typeref.getFullyQualifiedName(), caseNode.getKey()); - @SuppressWarnings("rawtypes") - ChoiceCaseCodecImpl value = new ChoiceCaseCodecImpl(); - typeToCaseCodecs.putIfAbsent(typeref, value); - continue; - } - @SuppressWarnings("rawtypes") - ChoiceCaseCodecImpl value = new ChoiceCaseCodecImpl(node); - typeToCaseCodecs.putIfAbsent(typeref, value); + synchronized (caseTypeToCaseSchema) { + caseTypeToCaseSchema.putAll(context.getCaseTypeToSchemas()); } } @@ -473,43 +448,9 @@ class LazyGeneratedCodecRegistry implements // Preconditions.checkState(oldCodec == null); BindingCodec, Object> delegate = newInstanceOf(choiceCodec); PublicChoiceCodecImpl newCodec = new PublicChoiceCodecImpl(delegate); + DispatchChoiceCodecImpl dispatchCodec = new DispatchChoiceCodecImpl(choiceClass); choiceCodecs.put(choiceClass, newCodec); - CodecMapping.setClassToCaseMap(choiceCodec, classToCaseRawCodec); - CodecMapping.setCompositeNodeToCaseMap(choiceCodec, newCodec.getCompositeToCase()); - - tryToCreateCasesCodecs(schema); - - } - - @Deprecated - private void tryToCreateCasesCodecs(final ChoiceNode schema) { - for (ChoiceCaseNode choiceCase : schema.getCases()) { - ChoiceCaseNode caseNode = choiceCase; - if (caseNode.isAddedByUses()) { - DataSchemaNode origCaseNode = SchemaContextUtil.findOriginal(caseNode, currentSchema); - if (origCaseNode instanceof ChoiceCaseNode) { - caseNode = (ChoiceCaseNode) origCaseNode; - } - } - SchemaPath path = caseNode.getPath(); - - GeneratedTypeBuilder type; - if (path != null && (type = pathToType.get(path)) != null) { - ReferencedTypeImpl typeref = new ReferencedTypeImpl(type.getPackageName(), type.getName()); - @SuppressWarnings("rawtypes") - ChoiceCaseCodecImpl partialCodec = typeToCaseCodecs.get(typeref); - if (partialCodec.getSchema() == null) { - partialCodec.setSchema(caseNode); - } - try { - Class caseClass = classLoadingStrategy.loadClass(type.getFullyQualifiedName()); - getCaseCodecFor(caseClass); - } catch (ClassNotFoundException e) { - LOG.trace("Could not proactivelly create case codec for {}", type, e); - } - } - } - + CodecMapping.setDispatchCodec(choiceCodec, dispatchCodec); } @Override @@ -530,7 +471,7 @@ class LazyGeneratedCodecRegistry implements // } } - public AugmentableDispatchCodec getAugmentableCodec(final Class dataClass) { + public synchronized AugmentableDispatchCodec getAugmentableCodec(final Class dataClass) { AugmentableDispatchCodec ret = augmentableCodecs.get(dataClass); if (ret != null) { return ret; @@ -675,43 +616,49 @@ class LazyGeneratedCodecRegistry implements // return implementation; } - protected final void adaptForPath(final InstanceIdentifier path) { + protected final synchronized void adaptForPath(final InstanceIdentifier path) { if (adaptedForPaths.contains(path)) { return; } /** - * We search in schema context if the use of this location aware codec (augmentable codec, case codec) - * makes sense on provided location (path) + * We search in schema context if the use of this location aware + * codec (augmentable codec, case codec) makes sense on provided + * location (path) * */ - Optional contextNode = BindingSchemaContextUtils.findDataNodeContainer(currentSchema, path); + Optional contextNode = BindingSchemaContextUtils.findDataNodeContainer(currentSchema, + path); /** - * If context node is present, this codec makes sense on provided location. + * If context node is present, this codec makes sense on provided + * location. * */ if (contextNode.isPresent()) { synchronized (this) { /** * - * We adapt (turn on / off) possible implementations of child codecs (augmentations, cases) - * based on this location. + * We adapt (turn on / off) possible implementations of + * child codecs (augmentations, cases) based on this + * location. * * */ + adaptForPathImpl(path, contextNode.get()); - try { + try { /** - * We trigger serialization of instance identifier, to make sure instance identifier - * codec is aware of combination of this path / augmentation / case + * We trigger serialization of instance identifier, to + * make sure instance identifier codec is aware of + * combination of this path / augmentation / case */ instanceIdentifierCodec.serialize(path); } catch (Exception e) { - LOG.warn("Exception during preparation of instance identifier codec for path {}.",path,e); + LOG.warn("Exception during preparation of instance identifier codec for path {}.", path, e); } adaptedForPaths.add(path); } } else { - LOG.debug("Context node (parent node) not found for {}",path); + LOG.debug("Context node (parent node) not found for {}", path); } } @@ -725,41 +672,21 @@ class LazyGeneratedCodecRegistry implements // @SuppressWarnings("rawtypes") private static class ChoiceCaseCodecImpl implements ChoiceCaseCodec, // Delegator, LocationAwareBindingCodec, ValueWithQName> { - private boolean augmenting; - private boolean uses; - private BindingCodec delegate; - - private Set validNames; - private Set validQNames; - private ChoiceCaseNode schema; - private Set> applicableLocations; + private final BindingCodec delegate; + private final ChoiceCaseNode schema; + private final Map, ChoiceCaseNode> instantiatedLocations; + private final Class dataType; @Override public boolean isApplicable(final InstanceIdentifier location) { - return applicableLocations.contains(location); + return instantiatedLocations.containsKey(location); } - public void setSchema(final ChoiceCaseNode caseNode) { + public ChoiceCaseCodecImpl(final Class caseClass, final ChoiceCaseNode caseNode, final BindingCodec newInstance) { + this.delegate = newInstance; + this.dataType = caseClass; this.schema = caseNode; - validNames = new HashSet<>(); - validQNames = new HashSet<>(); - for (DataSchemaNode node : caseNode.getChildNodes()) { - QName qname = node.getQName(); - validQNames.add(qname); - validNames.add(qname.getLocalName()); - } - augmenting = caseNode.isAugmenting(); - uses = caseNode.isAddedByUses(); - applicableLocations = new HashSet<>(); - } - - public ChoiceCaseCodecImpl() { - this.delegate = NOT_READY_CODEC; - } - - public ChoiceCaseCodecImpl(final ChoiceCaseNode caseNode) { - this.delegate = NOT_READY_CODEC; - setSchema(caseNode); + instantiatedLocations = new HashMap<>(); } @Override @@ -769,7 +696,19 @@ class LazyGeneratedCodecRegistry implements // @Override public ValueWithQName deserialize(final Node input, final InstanceIdentifier bindingIdentifier) { - throw new UnsupportedOperationException("Direct invocation of this codec is not allowed."); + if (input == null) { + return null; + } + QName qname = input.getNodeType(); + synchronized (instantiatedLocations) { + ChoiceCaseNode instantiation = instantiatedLocations.get(bindingIdentifier); + if (instantiation != null) { + qname = instantiatedLocations.get(bindingIdentifier).getQName(); + } + } + @SuppressWarnings("unchecked") + T value = (T) getDelegate().deserialize(new SimpleEntry(qname, input), bindingIdentifier); + return new ValueWithQName(qname, value); } @Override @@ -782,60 +721,54 @@ class LazyGeneratedCodecRegistry implements // return delegate; } - public void setDelegate(final BindingCodec delegate) { - this.delegate = delegate; - } - public ChoiceCaseNode getSchema() { return schema; } @Override + @Deprecated public boolean isAcceptable(final Node input) { - if (input instanceof CompositeNode) { - if (augmenting && !uses) { - return checkAugmenting((CompositeNode) input); - } else { - return checkLocal((CompositeNode) input); - } - } - return false; + return checkAgainstSchema(schema, input); } - @SuppressWarnings("deprecation") - private boolean checkLocal(final CompositeNode input) { - QName parent = input.getNodeType(); - for (Node childNode : input.getChildren()) { - QName child = childNode.getNodeType(); - if (!Objects.equals(parent.getNamespace(), child.getNamespace()) - || !Objects.equals(parent.getRevision(), child.getRevision())) { - continue; - } - if (validNames.contains(child.getLocalName())) { - return true; + private static boolean checkAgainstSchema(final ChoiceCaseNode schema, final Node node) { + if (node instanceof CompositeNode) { + CompositeNode input = (CompositeNode) node; + for (Node childNode : input.getValue()) { + QName child = childNode.getNodeType(); + if (schema.getDataChildByName(child) != null) { + return true; + } } } return false; } - @SuppressWarnings("deprecation") - private boolean checkAugmenting(final CompositeNode input) { - for (Node child : input.getChildren()) { - if (validQNames.contains(child.getNodeType())) { - return true; - } + @Override + public Class getDataType() { + return dataType; + } + + public void adaptForPath(final InstanceIdentifier augTarget, final ChoiceCaseNode choiceCaseNode) { + synchronized (instantiatedLocations) { + instantiatedLocations.put(augTarget, choiceCaseNode); } - return false; } - @Override - public Class getDataType() { - // TODO Auto-generated method stub - throw new UnsupportedOperationException("Not implemented Yet."); + public boolean isAcceptable(final InstanceIdentifier path, final CompositeNode input) { + ChoiceCaseNode instantiatedSchema = null; + synchronized (instantiatedLocations) { + instantiatedSchema = instantiatedLocations.get(path); + } + if (instantiatedSchema == null) { + return false; + } + return checkAgainstSchema(instantiatedSchema, input); } } - private static class PublicChoiceCodecImpl implements ChoiceCodec, Delegator, Object>> { + private static class PublicChoiceCodecImpl implements ChoiceCodec, + Delegator, Object>> { private final BindingCodec, Object> delegate; @@ -843,11 +776,8 @@ class LazyGeneratedCodecRegistry implements // private final Map> cases = Collections .synchronizedMap(new WeakHashMap>()); - private final CaseCompositeNodeMapFacade CompositeToCase; - public PublicChoiceCodecImpl(final BindingCodec, Object> delegate) { this.delegate = delegate; - this.CompositeToCase = new CaseCompositeNodeMapFacade(cases); } @Override @@ -865,10 +795,6 @@ class LazyGeneratedCodecRegistry implements // throw new UnsupportedOperationException("Direct invocation of this codec is not allowed."); } - public CaseCompositeNodeMapFacade getCompositeToCase() { - return CompositeToCase; - } - @Override public BindingCodec, Object> getDelegate() { return delegate; @@ -876,167 +802,146 @@ class LazyGeneratedCodecRegistry implements // } - @SuppressWarnings("unused") - private class DispatchChoiceCodecImpl extends LocationAwareDispatchCodec> { - - @Override - public Object deserialize(final Object input, @SuppressWarnings("rawtypes") final InstanceIdentifier bindingIdentifier) { - // TODO Auto-generated method stub - return null; - } + class DispatchChoiceCodecImpl extends LocationAwareDispatchCodec> { + private final Class choiceType; + private final QName choiceName; - @Override - public Object serialize(final Object input) { - // TODO Auto-generated method stub - return null; + private DispatchChoiceCodecImpl(final Class type) { + choiceType = type; + choiceName = BindingReflections.findQName(type); } @Override - protected ChoiceCaseCodecImpl tryToLoadImplementation(final Class inputType) { - return getCaseCodecFor(inputType); - } - - @Override - protected void tryToLoadImplementations() { - // TODO Auto-generated method stub - - } - - @Override - protected void adaptForPathImpl(final InstanceIdentifier path, final DataNodeContainer ctx) { - // TODO Auto-generated method stub - - } - } - - @SuppressWarnings("rawtypes") - private class CaseClassMapFacade extends MapFacadeBase { - - @Override - public Set>> entrySet() { - return Collections.emptySet(); - } - - @Override - public BindingCodec get(final Object key) { - if (key instanceof Class) { - Class cls = (Class) key; - // bindingClassEncountered(cls); - ChoiceCaseCodecImpl caseCodec = getCaseCodecFor(cls); - return caseCodec.getDelegate(); - } - return null; - } - } - - @SuppressWarnings("rawtypes") - private static class CaseCompositeNodeMapFacade extends MapFacadeBase { - - final Map> choiceCases; - - public CaseCompositeNodeMapFacade(final Map> choiceCases) { - this.choiceCases = choiceCases; - } + public Object deserialize(final Object input, @SuppressWarnings("rawtypes") final InstanceIdentifier path) { + adaptForPath(path); - @Override - public BindingCodec get(final Object key) { - if (!(key instanceof CompositeNode)) { - return null; - } - for (Entry> entry : choiceCases.entrySet()) { - ChoiceCaseCodecImpl codec = entry.getValue(); - if (codec.isAcceptable((CompositeNode) key)) { - return codec.getDelegate(); + if (input instanceof CompositeNode) { + List>> codecs = new ArrayList<>(getImplementations().entrySet()); + for (Entry> codec : codecs) { + ChoiceCaseCodecImpl caseCodec = codec.getValue(); + if (caseCodec.isAcceptable(path, (CompositeNode) input)) { + ValueWithQName value = caseCodec.deserialize((CompositeNode) input, path); + if (value != null) { + return value.getValue(); + } + return null; + } } } return null; } - } - - /** - * This map is used as only facade for - * {@link org.opendaylight.yangtools.yang.binding.BindingCodec} in different - * classloaders to retrieve codec dynamicly based on provided key. - * - * @param - * Key type - */ - @SuppressWarnings("rawtypes") - private abstract static class MapFacadeBase implements Map> { - - @Override - public boolean containsKey(final Object key) { - return get(key) != null; - } - - @Override - public void clear() { - throw notModifiable(); - } - - @Override - public boolean equals(final Object obj) { - return super.equals(obj); - } - - @Override - public BindingCodec remove(final Object key) { - return null; - } - + @SuppressWarnings("unchecked") @Override - public int size() { - return 0; + public Object serialize(final Object input) { + Preconditions.checkArgument(input instanceof Map.Entry, "Input must be QName, Value"); + @SuppressWarnings("rawtypes") + Object inputValue = ((Map.Entry) input).getValue(); + Preconditions.checkArgument(inputValue instanceof DataObject); + Class inputType = ((DataObject) inputValue).getImplementedInterface(); + ChoiceCaseCodecImpl codec = tryToLoadImplementation(inputType); + Preconditions.checkState(codec != null, "Unable to get codec for %s", inputType); + if(isAugmenting(codec.getSchema())) { + // If choice is augmenting we use QName which defined this augmentation + return codec.getDelegate().serialize(new ValueWithQName<>(codec.getSchema().getQName(), inputValue)); + } + return codec.getDelegate().serialize(input); } - @Override - public Collection> values() { - return Collections.emptySet(); + private boolean isAugmenting(final ChoiceCaseNode schema) { + if(schema.isAugmenting()) { + return true; + } + QName parentQName = Iterables.get(schema.getPath().getPathTowardsRoot(), 2); // choice QName + if(!parentQName.getNamespace().equals(schema.getQName().getNamespace())) { + return true; + } + return false; } - private UnsupportedOperationException notModifiable() { - return new UnsupportedOperationException("Not externally modifiable."); + @SuppressWarnings("rawtypes") + protected Optional tryToLoadImplementation(final Type potential) { + try { + @SuppressWarnings("unchecked") + Class clazz = (Class) classLoadingStrategy + .loadClass(potential); + ChoiceCaseCodecImpl codec = tryToLoadImplementation(clazz); + addImplementation(codec); + return Optional.of(codec); + } catch (ClassNotFoundException e) { + LOG.warn("Failed to find class for choice {}", potential, e); + } + return Optional.absent(); } @Override - public BindingCodec, Object> put(final T key, final BindingCodec value) { - throw notModifiable(); + protected ChoiceCaseCodecImpl tryToLoadImplementation(final Class inputType) { + ChoiceCaseCodecImpl codec = getCaseCodecFor(inputType); + addImplementation(codec); + return codec; } @Override - public void putAll(final Map> m) { - throw notModifiable(); + protected void tryToLoadImplementations() { + Type type = referencedType(choiceType); + Collection potentialCases; + synchronized (choiceToCases) { + potentialCases = choiceToCases.get(type); + } + for (Type potential : potentialCases) { + try { + tryToLoadImplementation(potential); + } catch (CodeGenerationException e) { + LOG.warn("Failed to proactively generate choice code for {}", type, e); + } + } } @Override - public int hashCode() { - return super.hashCode(); + protected void adaptForPathImpl(final InstanceIdentifier augTarget, final DataNodeContainer ctxNode) { + Optional newChoice = findInstantiatedChoice(ctxNode, choiceName); + tryToLoadImplementations(); + if (newChoice.isPresent()) { + for (@SuppressWarnings("rawtypes") + Entry> codec : getImplementations().entrySet()) { + ChoiceCaseCodecImpl caseCodec = codec.getValue(); + Optional instantiatedSchema = findInstantiatedCase(newChoice.get(), + caseCodec.getSchema()); + if (instantiatedSchema.isPresent()) { + caseCodec.adaptForPath(augTarget, instantiatedSchema.get()); + } + } + } } - @Override - public boolean isEmpty() { - return true; - } + private Optional findInstantiatedChoice(final DataNodeContainer ctxNode, final QName choiceName) { + DataSchemaNode potential = ctxNode.getDataChildByName(choiceName); + if (potential == null) { + potential = ctxNode.getDataChildByName(choiceName.getLocalName()); + } - @Override - public Set keySet() { - return Collections.emptySet(); - } + if (potential instanceof ChoiceNode) { + return Optional.of((ChoiceNode) potential); + } - @Override - public Set>> entrySet() { - return Collections.emptySet(); + return Optional.absent(); } - @Override - public boolean containsValue(final Object value) { - return false; + private Optional findInstantiatedCase(final ChoiceNode newChoice, final ChoiceCaseNode schema) { + ChoiceCaseNode potential = newChoice.getCaseNodeByName(schema.getQName()); + if (potential != null) { + return Optional.of(potential); + } + // FIXME: Probably requires more extensive check + // e.g. we have one choice and two augmentations from different + // modules using same local name + // but different namespace / contents + return Optional.fromNullable(newChoice.getCaseNodeByName(schema.getQName().getLocalName())); } } @SuppressWarnings({ "rawtypes", "unchecked" }) - private class AugmentableDispatchCodec extends LocationAwareDispatchCodec { + class AugmentableDispatchCodec extends LocationAwareDispatchCodec { private final Class augmentableType; @@ -1124,7 +1029,7 @@ class LazyGeneratedCodecRegistry implements // protected void tryToLoadImplementations() { Type type = referencedType(augmentableType); Collection potentialAugmentations; - synchronized(augmentableToAugmentations) { + synchronized (augmentableToAugmentations) { potentialAugmentations = new ArrayList(augmentableToAugmentations.get(type)); } for (Type potential : potentialAugmentations) { @@ -1137,37 +1042,74 @@ class LazyGeneratedCodecRegistry implements // } @Override - protected void adaptForPathImpl(final InstanceIdentifier path, final DataNodeContainer ctxNode) { + protected void adaptForPathImpl(final InstanceIdentifier augTarget, final DataNodeContainer ctxNode) { if (ctxNode instanceof AugmentationTarget) { Set availableAugmentations = ((AugmentationTarget) ctxNode) .getAvailableAugmentations(); if (!availableAugmentations.isEmpty()) { - updateAugmentationMapping(path,availableAugmentations); + updateAugmentationMapping(augTarget, availableAugmentations); } } } - private void updateAugmentationMapping(final InstanceIdentifier path, final Set availableAugmentations) { + /** + * + * Adapts augmentation codec for specific provider location (target) + * + * Since augmentation are not forward-referencing and may be discovered + * during runtime, we need to adapt {@link AugmentableDispatchCodec}, + * {@link AugmentationCodecWrapper} and {@link InstanceIdentifierCodec} + * for this newly discovered location where augmentation may be used. + * + * Adaptation consists of: + *
    + *
  1. scan of available (valid) augmentations for + * current location + *
  2. lookup for Java classes derived from this augmentations + *
  3. generation of missing codecs + *
  4. updating Augmentation codecs to work with new location + *
  5. updating Instance Identifier to work with new location + * + */ + private void updateAugmentationMapping(final InstanceIdentifier augTarget, + final Set availableAugmentations) { for (AugmentationSchema aug : availableAugmentations) { Type potentialType = getTypeForAugmentation(aug); if (potentialType != null) { Optional potentialImpl = tryToLoadImplementation(potentialType); if (potentialImpl.isPresent()) { - potentialImpl.get().addApplicableFor(path,aug); + potentialImpl.get().addApplicableFor(augTarget, aug); + Class augType = potentialImpl.get().getDataType(); + InstanceIdentifier augPath = augTarget.augmentation(augType); + try { + + org.opendaylight.yangtools.yang.data.api.InstanceIdentifier domPath = getInstanceIdentifierCodec().serialize(augPath); + if(domPath == null) { + LOG.error("Unable to serialize instance identifier for {}",augPath); + } + } catch (Exception e) { + LOG.error("Unable to serialize instance identifiers for {}",augPath,e); + } + } } else { - LOG.warn("Could not find generated type for augmentation {} with children {}", aug, aug.getChildNodes()); + // Omits warning for empty augmentations since they are not represented in data + if(!aug.getChildNodes().isEmpty()) { + LOG.warn("Could not find generated type for augmentation {} with children {}", aug, + aug.getChildNodes()); + } } } - availableAugmentations.toString(); } + + private Type getTypeForAugmentation(final AugmentationSchema aug) { Optional currentAug = Optional.of(aug); - while(currentAug.isPresent()) { + while (currentAug.isPresent()) { Type potentialType = typeToAugment.inverse().get(currentAug.get()); - if(potentialType != null) { + if (potentialType != null) { return potentialType; } currentAug = currentAug.get().getOriginalDefinition(); @@ -1176,56 +1118,26 @@ class LazyGeneratedCodecRegistry implements // } } - @SuppressWarnings({ "rawtypes", "unchecked" }) - private static class LateMixinCodec implements BindingCodec, Delegator { - - private BindingCodec delegate; - - @Override - public BindingCodec getDelegate() { - if (delegate == null) { - throw new IllegalStateException("Codec not initialized yet."); - } - return delegate; - } - - @Override - public Object deserialize(final Object input) { - return getDelegate().deserialize(input); - } - - @Override - public Object deserialize(final Object input, final InstanceIdentifier bindingIdentifier) { - return getDelegate().deserialize(input, bindingIdentifier); - } - - @Override - public Object serialize(final Object input) { - return getDelegate().serialize(input); - } - - } - @SuppressWarnings("rawtypes") private static class AugmentationCodecWrapper> implements AugmentationCodec, Delegator, LocationAwareBindingCodec, ValueWithQName> { private final BindingCodec delegate; private final QName augmentationQName; - private final Multimap,QName> validAugmentationTargets; + private final Multimap, QName> validAugmentationTargets; private final Class augmentationType; - public AugmentationCodecWrapper(final BindingCodec, Object> rawCodec, - final InstanceIdentifier targetId, final Class dataType) { + public AugmentationCodecWrapper(final BindingCodec, Object> rawCodec, final Class dataType) { this.delegate = rawCodec; this.augmentationType = dataType; this.augmentationQName = BindingReflections.findQName(rawCodec.getClass()); - this.validAugmentationTargets = Multimaps.synchronizedSetMultimap(HashMultimap.,QName>create()); + this.validAugmentationTargets = Multimaps.synchronizedSetMultimap(HashMultimap + ., QName> create()); } public void addApplicableFor(final InstanceIdentifier path, final AugmentationSchema aug) { - for(DataSchemaNode child : aug.getChildNodes()) { - validAugmentationTargets.put(path,child.getQName()); + for (DataSchemaNode child : aug.getChildNodes()) { + validAugmentationTargets.put(path, child.getQName()); } } @@ -1321,7 +1233,7 @@ class LazyGeneratedCodecRegistry implements // } @Override - public Object deserialize(final Object input,final InstanceIdentifier bindingIdentifier) { + public Object deserialize(final Object input, final InstanceIdentifier bindingIdentifier) { Type type = qnamesToIdentityMap.get(input); if (type == null) { return null;