X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=binding%2Fmdsal-binding-dom-codec%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fmdsal%2Fbinding%2Fdom%2Fcodec%2Fimpl%2FDataObjectCodecContext.java;h=2fa41e9585c2599e14dd84413affe9c588828c1b;hb=172f47ef9f3c164359da032e5768ebb12f3feb14;hp=8e513b9cee14b6476f89da5d694f1ee562cccbce;hpb=65200ab6676fc5a3d7efaa28d0309450f0223980;p=mdsal.git diff --git a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataObjectCodecContext.java b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataObjectCodecContext.java index 8e513b9cee..2fa41e9585 100644 --- a/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataObjectCodecContext.java +++ b/binding/mdsal-binding-dom-codec/src/main/java/org/opendaylight/mdsal/binding/dom/codec/impl/DataObjectCodecContext.java @@ -14,6 +14,7 @@ import com.google.common.annotations.Beta; import com.google.common.base.Throwables; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap.Builder; +import com.google.common.collect.ImmutableSet; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; @@ -27,16 +28,22 @@ import java.util.Optional; import org.eclipse.jdt.annotation.NonNull; import org.eclipse.jdt.annotation.Nullable; import org.opendaylight.mdsal.binding.dom.codec.api.IncorrectNestingException; +import org.opendaylight.mdsal.binding.model.api.GeneratedType; +import org.opendaylight.mdsal.binding.model.api.JavaTypeName; import org.opendaylight.mdsal.binding.model.api.Type; +import org.opendaylight.mdsal.binding.runtime.api.AugmentRuntimeType; +import org.opendaylight.mdsal.binding.runtime.api.AugmentableRuntimeType; import org.opendaylight.mdsal.binding.runtime.api.BindingRuntimeContext; +import org.opendaylight.mdsal.binding.runtime.api.ChoiceRuntimeType; +import org.opendaylight.mdsal.binding.runtime.api.CompositeRuntimeType; import org.opendaylight.mdsal.binding.spec.reflect.BindingReflections; import org.opendaylight.yangtools.yang.binding.Augmentable; import org.opendaylight.yangtools.yang.binding.Augmentation; +import org.opendaylight.yangtools.yang.binding.DataContainer; import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier.Item; import org.opendaylight.yangtools.yang.binding.OpaqueObject; -import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier; import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; @@ -45,12 +52,9 @@ import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgum import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode; import org.opendaylight.yangtools.yang.data.api.schema.DistinctNodeContainer; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; -import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode; -import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; -import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; -import org.opendaylight.yangtools.yang.model.api.DerivableSchemaNode; import org.opendaylight.yangtools.yang.model.api.DocumentedNode.WithStatus; -import org.opendaylight.yangtools.yang.model.api.SchemaNode; +import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement; +import org.opendaylight.yangtools.yang.model.api.stmt.SchemaTreeEffectiveStatement; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -58,7 +62,7 @@ import org.slf4j.LoggerFactory; * This class is an implementation detail. It is public only due to technical reasons and may change at any time. */ @Beta -public abstract class DataObjectCodecContext +public abstract class DataObjectCodecContext extends DataContainerCodecContext { private static final Logger LOG = LoggerFactory.getLogger(DataObjectCodecContext.class); private static final MethodType CONSTRUCTOR_TYPE = MethodType.methodType(void.class, @@ -99,8 +103,10 @@ public abstract class DataObjectCodecContext bindingClass = getBindingClass(); - final ImmutableMap tmpLeaves = factory().getLeafNodes(bindingClass, getSchema()); - final Map, Method> clsToMethod = BindingReflections.getChildrenClassToMethod(bindingClass); + final ImmutableMap tmpLeaves = factory().getLeafNodes(bindingClass, + getType().statement()); + final Map, Method> clsToMethod = + BindingReflections.getChildrenClassToMethod(bindingClass); final Map byYangBuilder = new HashMap<>(); final Map, DataContainerCodecPrototype> byStreamClassBuilder = new HashMap<>(); @@ -109,29 +115,32 @@ public abstract class DataObjectCodecContext leafChildBuilder = ImmutableMap.builderWithExpectedSize(tmpLeaves.size()); - for (final Entry entry : tmpLeaves.entrySet()) { - final ValueNodeCodecContext leaf = entry.getValue(); + for (final ValueNodeCodecContext leaf : tmpLeaves.values()) { leafChildBuilder.put(leaf.getSchema().getQName().getLocalName(), leaf); byYangBuilder.put(leaf.getDomPathArgument(), leaf); } this.leafChild = leafChildBuilder.build(); final Map> tmpDataObjects = new HashMap<>(); - for (final Entry, Method> childDataObj : clsToMethod.entrySet()) { + for (final Entry, Method> childDataObj : clsToMethod.entrySet()) { final Method method = childDataObj.getValue(); verify(!method.isDefault(), "Unexpected default method %s in %s", method, bindingClass); - final Class retClass = childDataObj.getKey(); + final Class retClass = childDataObj.getKey(); if (OpaqueObject.class.isAssignableFrom(retClass)) { // Filter OpaqueObjects, they are not containers continue; } final DataContainerCodecPrototype childProto = loadChildPrototype(retClass); - tmpDataObjects.put(method, childProto.getBindingClass()); - byStreamClassBuilder.put(childProto.getBindingClass(), childProto); + final Class childClass = childProto.getBindingClass(); + tmpDataObjects.put(method, childClass); + byStreamClassBuilder.put(childClass, childProto); byYangBuilder.put(childProto.getYangArg(), childProto); - if (childProto.isChoice()) { + + // FIXME: It really feels like we should be specializing DataContainerCodecPrototype so as to ditch + // createInstance() and then we could do an instanceof check instead. + if (childProto.getType() instanceof ChoiceRuntimeType) { final ChoiceNodeCodecContext choice = (ChoiceNodeCodecContext) childProto.get(); for (final Class cazeChild : choice.getCaseChildrenClasses()) { byBindingArgClassBuilder.put(cazeChild, childProto); @@ -149,13 +158,17 @@ public abstract class DataObjectCodecContext possibleAugmentations; + final List possibleAugmentations; if (Augmentable.class.isAssignableFrom(bindingClass)) { - possibleAugmentations = factory().getRuntimeContext().getAvailableAugmentationTypes(getSchema()); + // Verify we have the appropriate backing runtimeType + final var type = getType(); + verify(type instanceof AugmentableRuntimeType, "Unexpected type %s backing augmenable %s", type, + bindingClass); + possibleAugmentations = ((AugmentableRuntimeType) type).augments(); generatedClass = CodecDataObjectGenerator.generateAugmentable(prototype.getFactory().getLoader(), bindingClass, tmpLeaves, tmpDataObjects, keyMethod); } else { - possibleAugmentations = ImmutableMap.of(); + possibleAugmentations = List.of(); generatedClass = CodecDataObjectGenerator.generate(prototype.getFactory().getLoader(), bindingClass, tmpLeaves, tmpDataObjects, keyMethod); } @@ -163,15 +176,17 @@ public abstract class DataObjectCodecContext> augByYang = new HashMap<>(); final Map, DataContainerCodecPrototype> augByStream = new HashMap<>(); - for (final Type augment : possibleAugmentations.values()) { - final DataContainerCodecPrototype augProto = getAugmentationPrototype(augment); - final PathArgument augYangArg = augProto.getYangArg(); - if (augByYang.putIfAbsent(augYangArg, augProto) == null) { - LOG.trace("Discovered new YANG mapping {} -> {} in {}", augYangArg, augProto, this); - } - final Class augBindingClass = augProto.getBindingClass(); - if (augByStream.putIfAbsent(augBindingClass, augProto) == null) { - LOG.trace("Discovered new class mapping {} -> {} in {}", augBindingClass, augProto, this); + for (final AugmentRuntimeType augment : possibleAugmentations) { + final DataContainerCodecPrototype augProto = loadAugmentPrototype(augment); + if (augProto != null) { + final PathArgument augYangArg = augProto.getYangArg(); + if (augByYang.putIfAbsent(augYangArg, augProto) == null) { + LOG.trace("Discovered new YANG mapping {} -> {} in {}", augYangArg, augProto, this); + } + final Class augBindingClass = augProto.getBindingClass(); + if (augByStream.putIfAbsent(augBindingClass, augProto) == null) { + LOG.trace("Discovered new class mapping {} -> {} in {}", augBindingClass, augProto, this); + } } } augmentationByYang = ImmutableMap.copyOf(augByYang); @@ -187,12 +202,17 @@ public abstract class DataObjectCodecContext DataContainerCodecContext streamChild(final Class childClass) { - final DataContainerCodecPrototype childProto = streamChildPrototype(childClass); - return (DataContainerCodecContext) childNonNull(childProto, childClass, " Child %s is not valid child.", - childClass).get(); + return (DataContainerCodecContext) childNonNull(streamChildPrototype(childClass), childClass, + "Child %s is not valid child of %s", getBindingClass(), childClass).get(); } private DataContainerCodecPrototype streamChildPrototype(final Class childClass) { @@ -271,65 +291,23 @@ public abstract class DataObjectCodecContext loadChildPrototype(final Class childClass) { - final DataSchemaNode origDef = factory().getRuntimeContext().getSchemaDefinition(childClass); - // Direct instantiation or use in same module in which grouping - // was defined. - DataSchemaNode sameName; - try { - sameName = getSchema().dataChildByName(origDef.getQName()); - } catch (final IllegalArgumentException e) { - LOG.trace("Failed to find schema for {}", origDef, e); - sameName = null; - } - final DataSchemaNode childSchema; - if (sameName != null) { - // Check if it is: - // - exactly same schema node, or - // - instantiated node was added via uses statement and is instantiation of same grouping - if (origDef.equals(sameName) || origDef.equals(getRootOriginalIfPossible(sameName))) { - childSchema = sameName; - } else { - // Node has same name, but clearly is different - childSchema = null; - } - } else { - // We are looking for instantiation via uses in other module - final QName instantiedName = origDef.getQName().bindTo(namespace()); - final DataSchemaNode potential = getSchema().dataChildByName(instantiedName); - // We check if it is really instantiated from same definition as class was derived - if (potential != null && origDef.equals(getRootOriginalIfPossible(potential))) { - childSchema = potential; - } else { - childSchema = null; - } - } - final DataSchemaNode nonNullChild = - childNonNull(childSchema, childClass, "Node %s does not have child named %s", getSchema(), childClass); - return DataContainerCodecPrototype.from(createBindingArg(childClass, nonNullChild), nonNullChild, factory()); - } + private DataContainerCodecPrototype loadChildPrototype(final Class childClass) { + final var type = getType(); + final var child = childNonNull(type.bindingChild(JavaTypeName.create(childClass)), childClass, + "Node %s does not have child named %s", type, childClass); - private static SchemaNode getRootOriginalIfPossible(final SchemaNode data) { - Optional previous = Optional.empty(); - Optional next = getOriginalIfPossible(data); - while (next.isPresent()) { - previous = next; - next = getOriginalIfPossible(next.get()); - } - return previous.orElse(null); - } - - private static Optional getOriginalIfPossible(final SchemaNode node) { - if (node instanceof DerivableSchemaNode) { - @SuppressWarnings("unchecked") - final Optional ret = (Optional) ((DerivableSchemaNode) node).getOriginal(); - return ret; - } - return Optional.empty(); + return DataContainerCodecPrototype.from(createBindingArg(childClass, child.statement()), + (CompositeRuntimeType) child, factory()); } + // FIXME: MDSAL-697: move this method into BindingRuntimeContext + // This method is only called from loadChildPrototype() and exists only to be overridden by + // CaseNodeCodecContext. Since we are providing childClass and our schema to BindingRuntimeContext + // and receiving childSchema from it via findChildSchemaDefinition, we should be able to receive + // the equivalent of Map.Entry, along with the override we create here. One + // more input we may need to provide is our bindingClass(). @SuppressWarnings("unchecked") - Item createBindingArg(final Class childClass, final DataSchemaNode childSchema) { + Item createBindingArg(final Class childClass, final EffectiveStatement childSchema) { return Item.of((Class) childClass); } @@ -382,13 +360,14 @@ public abstract class DataObjectCodecContext, DataContainerCodecPrototype>) + MISMATCHED_AUGMENTED.compareAndExchangeRelease(this, expected, newMismatched); if (witness == expected) { LOG.trace("Cached mismatched augmentation {} -> {} in {}", childClass, prototype, this); return prototype; } - expected = (ImmutableMap, DataContainerCodecPrototype>) witness; + expected = witness; final DataContainerCodecPrototype existing = expected.get(childClass); if (existing != null) { LOG.trace("Using raced mismatched augmentation {} -> {} in {}", childClass, existing, this); @@ -409,19 +388,29 @@ public abstract class DataObjectCodecContext getAugmentationPrototype(final Type value) { - final BindingRuntimeContext ctx = factory().getRuntimeContext(); + private @Nullable DataContainerCodecPrototype loadAugmentPrototype(final AugmentRuntimeType augment) { + // FIXME: in face of deviations this code should be looking at declared view, i.e. all possibilities at augment + // declaration site + final var possibleChildren = augment.statement() + .streamEffectiveSubstatements(SchemaTreeEffectiveStatement.class) + .map(SchemaTreeEffectiveStatement::getIdentifier) + .collect(ImmutableSet.toImmutableSet()); + if (possibleChildren.isEmpty()) { + return null; + } + final var factory = factory(); + final GeneratedType javaType = augment.javaType(); final Class> augClass; try { - augClass = ctx.loadClass(value); + augClass = factory.getRuntimeContext().loadClass(javaType); } catch (final ClassNotFoundException e) { - throw new IllegalStateException("RuntimeContext references type " + value + " but failed to its class", e); + throw new IllegalStateException( + "RuntimeContext references type " + javaType + " but failed to load its class", e); } - final Entry augSchema = - ctx.getResolvedAugmentationSchema(getSchema(), augClass); - return DataContainerCodecPrototype.from(augClass, augSchema.getKey(), augSchema.getValue(), factory()); + return DataContainerCodecPrototype.from(augClass, new AugmentationIdentifier(possibleChildren), augment, + factory); } @SuppressWarnings("checkstyle:illegalCatch") @@ -452,9 +441,16 @@ public abstract class DataObjectCodecContext value : augmentationByStream.values()) { - final NormalizedNode augData = data.childByArg(value.getYangArg()); - if (augData != null) { - map.put(value.getBindingClass(), value.get().deserializeObject(augData)); + final var augClass = value.getBindingClass(); + // Do not perform duplicate deserialization if we have already created the corresponding augmentation + // and validate whether the proposed augmentation is valid ion this instantiation context. + if (!map.containsKey(augClass) + && ((AugmentableRuntimeType) getType()).augments().contains(value.getType())) { + final NormalizedNode augData = data.childByArg(value.getYangArg()); + if (augData != null) { + // ... make sure we do not replace an e + map.putIfAbsent(augClass, value.get().deserializeObject(augData)); + } } } return map;