X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=yang%2Fyang-data-util%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fyangtools%2Fyang%2Fdata%2Futil%2FCompositeNodeDataWithSchema.java;h=8d6bf20a5db4acb109bf9b9af1194dd1b2e4575c;hb=bb60da5fe2d1928defb46ed92b290cfff93dcd81;hp=d3a53bd520dfb1612d4b0bf954b6fb7e961c29f9;hpb=31d5ed0c664e8fbd793538c4e4546204847c779e;p=yangtools.git diff --git a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/CompositeNodeDataWithSchema.java b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/CompositeNodeDataWithSchema.java index d3a53bd520..8d6bf20a5d 100644 --- a/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/CompositeNodeDataWithSchema.java +++ b/yang/yang-data-util/src/main/java/org/opendaylight/yangtools/yang/data/util/CompositeNodeDataWithSchema.java @@ -7,11 +7,10 @@ */ package org.opendaylight.yangtools.yang.data.util; -import com.google.common.base.Function; -import com.google.common.base.Preconditions; +import static com.google.common.base.Preconditions.checkArgument; + +import com.google.common.annotations.Beta; import com.google.common.collect.ArrayListMultimap; -import com.google.common.collect.Collections2; -import com.google.common.collect.ImmutableSet; import com.google.common.collect.Multimap; import java.io.IOException; import java.util.ArrayList; @@ -19,103 +18,186 @@ import java.util.Collection; import java.util.Deque; import java.util.List; import java.util.Map.Entry; -import javax.annotation.Nonnull; -import org.opendaylight.yangtools.yang.common.QName; -import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier; -import org.opendaylight.yangtools.yang.data.api.schema.stream.SchemaAwareNormalizedNodeStreamWriter; -import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode; -import org.opendaylight.yangtools.yang.model.api.AugmentationSchema; +import org.eclipse.jdt.annotation.Nullable; +import org.opendaylight.yangtools.odlext.model.api.YangModeledAnyxmlSchemaNode; +import org.opendaylight.yangtools.rfc7952.data.api.StreamWriterMetadataExtension; +import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter; +import org.opendaylight.yangtools.yang.model.api.AnydataSchemaNode; +import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode; +import org.opendaylight.yangtools.yang.model.api.AugmentationSchemaNode; import org.opendaylight.yangtools.yang.model.api.AugmentationTarget; -import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode; +import org.opendaylight.yangtools.yang.model.api.CaseSchemaNode; import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode; -import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; +import org.opendaylight.yangtools.yang.model.api.ContainerLike; import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode; import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; -import org.opendaylight.yangtools.yang.model.api.YangModeledAnyXmlSchemaNode; /** - * A node which is composed of multiple simpler nodes. + * Utility class used for tracking parser state as needed by a StAX-like parser. + * This class is to be used only by respective XML and JSON parsers in yang-data-codec-xml and yang-data-codec-gson. + * + *

+ * Represents a node which is composed of multiple simpler nodes. */ -public class CompositeNodeDataWithSchema extends AbstractNodeDataWithSchema { +public class CompositeNodeDataWithSchema extends AbstractNodeDataWithSchema { + /** + * Policy on how child nodes should be treated when an attempt is made to add them multiple times. + */ + @Beta + public enum ChildReusePolicy { + /** + * Do not consider any existing nodes at all, just perform a straight append. Multiple occurrences of a child + * will result in multiple children being emitted. This is almost certainly the wrong policy unless the caller + * prevents such a situation from arising via some different mechanism. + */ + NOOP, + /** + * Do not allow duplicate definition of a child node. This would typically be used when a child cannot be + * encountered multiple times, but the caller does not make any provision to detect such a conflict. If a child + * node would end up being defined a second time, {@link DuplicateChildNodeRejectedException} is reported. + */ + REJECT { + @Override + AbstractNodeDataWithSchema appendChild(final Collection> view, + final AbstractNodeDataWithSchema newChild) { + final DataSchemaNode childSchema = newChild.getSchema(); + final AbstractNodeDataWithSchema existing = findExistingChild(view, childSchema); + if (existing != null) { + throw new DuplicateChildNodeRejectedException("Duplicate child " + childSchema.getQName()); + } + return super.appendChild(view, newChild); + } + }, + /** + * Reuse previously-defined child node. This is most appropriate when a child may be visited multiple times + * and the intent is to append content of each visit. A typical usage is list elements with RFC7950 XML + * encoding, where there is no encapsulating element and hence list entries may be interleaved with other + * children. + */ + REUSE { + @Override + AbstractNodeDataWithSchema appendChild(final Collection> view, + final AbstractNodeDataWithSchema newChild) { + final AbstractNodeDataWithSchema existing = findExistingChild(view, newChild.getSchema()); + return existing != null ? existing : super.appendChild(view, newChild); + } + }; + + AbstractNodeDataWithSchema appendChild(final Collection> view, + final AbstractNodeDataWithSchema newChild) { + view.add(newChild); + return newChild; + } + + static @Nullable AbstractNodeDataWithSchema findExistingChild( + final Collection> view, final DataSchemaNode childSchema) { + for (AbstractNodeDataWithSchema existing : view) { + if (childSchema.equals(existing.getSchema())) { + return existing; + } + } + return null; + } + } /** - * nodes which were added to schema via augmentation and are present in data input + * nodes which were added to schema via augmentation and are present in data input. */ - private final Multimap augmentationsToChild = ArrayListMultimap.create(); + private final Multimap> augmentationsToChild = + ArrayListMultimap.create(); /** * remaining data nodes (which aren't added via augment). Every of one them should have the same QName. */ - private final List children = new ArrayList<>(); + private final List> children = new ArrayList<>(); - public CompositeNodeDataWithSchema(final DataSchemaNode schema) { + public CompositeNodeDataWithSchema(final T schema) { super(schema); } - public AbstractNodeDataWithSchema addChild(final Deque schemas) { - Preconditions.checkArgument(!schemas.isEmpty(), "Expecting at least one schema"); + void addChild(final AbstractNodeDataWithSchema newChild) { + children.add(newChild); + } + + public final AbstractNodeDataWithSchema addChild(final Deque schemas, + final ChildReusePolicy policy) { + checkArgument(!schemas.isEmpty(), "Expecting at least one schema"); // Pop the first node... final DataSchemaNode schema = schemas.pop(); if (schemas.isEmpty()) { // Simple, direct node - return addChild(schema); + return addChild(schema, policy); } // The choice/case mess, reuse what we already popped final DataSchemaNode choiceCandidate = schema; - Preconditions.checkArgument(choiceCandidate instanceof ChoiceSchemaNode, - "Expected node of type ChoiceNode but was %s", choiceCandidate.getClass().getSimpleName()); + checkArgument(choiceCandidate instanceof ChoiceSchemaNode, "Expected node of type ChoiceNode but was %s", + choiceCandidate.getClass()); final ChoiceSchemaNode choiceNode = (ChoiceSchemaNode) choiceCandidate; final DataSchemaNode caseCandidate = schemas.pop(); - Preconditions.checkArgument(caseCandidate instanceof ChoiceCaseNode, - "Expected node of type ChoiceCaseNode but was %s", caseCandidate.getClass().getSimpleName()); - final ChoiceCaseNode caseNode = (ChoiceCaseNode) caseCandidate; + checkArgument(caseCandidate instanceof CaseSchemaNode, "Expected node of type ChoiceCaseNode but was %s", + caseCandidate.getClass()); + final CaseSchemaNode caseNode = (CaseSchemaNode) caseCandidate; - AugmentationSchema augSchema = null; + final AugmentationSchemaNode augSchema; if (choiceCandidate.isAugmenting()) { augSchema = findCorrespondingAugment(getSchema(), choiceCandidate); + } else { + augSchema = null; } // looking for existing choice - final Collection childNodes; + final Collection> childNodes; if (augSchema != null) { childNodes = augmentationsToChild.get(augSchema); } else { childNodes = children; } - CompositeNodeDataWithSchema caseNodeDataWithSchema = findChoice(childNodes, choiceCandidate, caseCandidate); + CompositeNodeDataWithSchema caseNodeDataWithSchema = findChoice(childNodes, choiceCandidate, caseCandidate); if (caseNodeDataWithSchema == null) { ChoiceNodeDataWithSchema choiceNodeDataWithSchema = new ChoiceNodeDataWithSchema(choiceNode); childNodes.add(choiceNodeDataWithSchema); - caseNodeDataWithSchema = choiceNodeDataWithSchema.addCompositeChild(caseNode); + caseNodeDataWithSchema = choiceNodeDataWithSchema.addCompositeChild(caseNode, ChildReusePolicy.NOOP); } - return caseNodeDataWithSchema.addChild(schemas); + return caseNodeDataWithSchema.addChild(schemas, policy); } - private AbstractNodeDataWithSchema addSimpleChild(final DataSchemaNode schema) { - SimpleNodeDataWithSchema newChild = null; + private AbstractNodeDataWithSchema addChild(final DataSchemaNode schema, final ChildReusePolicy policy) { + AbstractNodeDataWithSchema newChild = addSimpleChild(schema, policy); + return newChild == null ? addCompositeChild(schema, policy) : newChild; + } + + private AbstractNodeDataWithSchema addSimpleChild(final DataSchemaNode schema, final ChildReusePolicy policy) { + final SimpleNodeDataWithSchema newChild; if (schema instanceof LeafSchemaNode) { - newChild = new LeafNodeDataWithSchema(schema); - } else if (schema instanceof AnyXmlSchemaNode) { - // YangModeledAnyXmlSchemaNode is handled by addCompositeChild method. - if (schema instanceof YangModeledAnyXmlSchemaNode) { + newChild = new LeafNodeDataWithSchema((LeafSchemaNode) schema); + } else if (schema instanceof AnyxmlSchemaNode) { + // YangModeledAnyxmlSchemaNode is handled by addCompositeChild method. + if (schema instanceof YangModeledAnyxmlSchemaNode) { return null; } - newChild = new AnyXmlNodeDataWithSchema(schema); + newChild = new AnyXmlNodeDataWithSchema((AnyxmlSchemaNode) schema); + } else if (schema instanceof AnydataSchemaNode) { + newChild = new AnydataNodeDataWithSchema((AnydataSchemaNode) schema); } else { return null; } - AugmentationSchema augSchema = null; + final AugmentationSchemaNode augSchema; if (schema.isAugmenting()) { augSchema = findCorrespondingAugment(getSchema(), schema); + } else { + augSchema = null; } + + // FIXME: 7.0.0: use policy to determine if we should reuse or replace the child + if (augSchema != null) { augmentationsToChild.put(augSchema, newChild); } else { @@ -124,17 +206,18 @@ public class CompositeNodeDataWithSchema extends AbstractNodeDataWithSchema { return newChild; } - private static CaseNodeDataWithSchema findChoice(final Collection childNodes, + private static CaseNodeDataWithSchema findChoice(final Collection> childNodes, final DataSchemaNode choiceCandidate, final DataSchemaNode caseCandidate) { if (childNodes != null) { - for (AbstractNodeDataWithSchema nodeDataWithSchema : childNodes) { + for (AbstractNodeDataWithSchema nodeDataWithSchema : childNodes) { if (nodeDataWithSchema instanceof ChoiceNodeDataWithSchema && nodeDataWithSchema.getSchema().getQName().equals(choiceCandidate.getQName())) { CaseNodeDataWithSchema casePrevious = ((ChoiceNodeDataWithSchema) nodeDataWithSchema).getCase(); - Preconditions.checkArgument(casePrevious.getSchema().getQName().equals(caseCandidate.getQName()), - "Data from case %s are specified but other data from case %s were specified erlier. Data aren't from the same case.", - caseCandidate.getQName(), casePrevious.getSchema().getQName()); + checkArgument(casePrevious.getSchema().getQName().equals(caseCandidate.getQName()), + "Data from case %s are specified but other data from case %s were specified earlier." + + " Data aren't from the same case.", caseCandidate.getQName(), + casePrevious.getSchema().getQName()); return casePrevious; } @@ -143,41 +226,31 @@ public class CompositeNodeDataWithSchema extends AbstractNodeDataWithSchema { return null; } - AbstractNodeDataWithSchema addCompositeChild(final DataSchemaNode schema) { - final CompositeNodeDataWithSchema newChild; + AbstractNodeDataWithSchema addCompositeChild(final DataSchemaNode schema, final ChildReusePolicy policy) { + final CompositeNodeDataWithSchema newChild; if (schema instanceof ListSchemaNode) { - newChild = new ListNodeDataWithSchema(schema); + newChild = new ListNodeDataWithSchema((ListSchemaNode) schema); } else if (schema instanceof LeafListSchemaNode) { - newChild = new LeafListNodeDataWithSchema(schema); - } else if (schema instanceof ContainerSchemaNode) { - newChild = new ContainerNodeDataWithSchema(schema); - } else if (schema instanceof YangModeledAnyXmlSchemaNode) { - newChild = new YangModeledAnyXmlNodeDataWithSchema((YangModeledAnyXmlSchemaNode)schema); + newChild = new LeafListNodeDataWithSchema((LeafListSchemaNode) schema); + } else if (schema instanceof ContainerLike) { + newChild = new ContainerNodeDataWithSchema((ContainerLike) schema); + } else if (schema instanceof YangModeledAnyxmlSchemaNode) { + newChild = new YangModeledAnyXmlNodeDataWithSchema((YangModeledAnyxmlSchemaNode)schema); } else { - newChild = new CompositeNodeDataWithSchema(schema); + newChild = new CompositeNodeDataWithSchema<>(schema); } - addCompositeChild(newChild); - return newChild; + return addCompositeChild(newChild, policy); } - void addCompositeChild(final CompositeNodeDataWithSchema newChild) { - AugmentationSchema augSchema = findCorrespondingAugment(getSchema(), newChild.getSchema()); - if (augSchema != null) { - augmentationsToChild.put(augSchema, newChild); - } else { - addChild(newChild); - } - } + final AbstractNodeDataWithSchema addCompositeChild(final CompositeNodeDataWithSchema newChild, + final ChildReusePolicy policy) { + final AugmentationSchemaNode augSchema = findCorrespondingAugment(getSchema(), newChild.getSchema()); + final Collection> view = augSchema == null ? children + : augmentationsToChild.get(augSchema); - private AbstractNodeDataWithSchema addChild(final DataSchemaNode schema) { - AbstractNodeDataWithSchema newChild = addSimpleChild(schema); - return newChild == null ? addCompositeChild(schema) : newChild; - } - - public void addChild(final AbstractNodeDataWithSchema newChild) { - children.add(newChild); + return policy.appendChild(view, newChild); } /** @@ -189,18 +262,21 @@ public class CompositeNodeDataWithSchema extends AbstractNodeDataWithSchema { } @Override - public void write(final SchemaAwareNormalizedNodeStreamWriter writer) throws IOException { - for (AbstractNodeDataWithSchema child : children) { - child.write(writer); + public void write(final NormalizedNodeStreamWriter writer, final StreamWriterMetadataExtension metaWriter) + throws IOException { + for (AbstractNodeDataWithSchema child : children) { + child.write(writer, metaWriter); } - for (Entry> augmentationToChild : augmentationsToChild.asMap().entrySet()) { - final Collection childsFromAgumentation = augmentationToChild.getValue(); + for (Entry>> augmentationToChild + : augmentationsToChild.asMap().entrySet()) { + final Collection> childsFromAgumentation = augmentationToChild.getValue(); if (!childsFromAgumentation.isEmpty()) { // FIXME: can we get the augmentation schema? - writer.startAugmentationNode(getNodeIdentifierForAugmentation(augmentationToChild.getKey())); + writer.startAugmentationNode(DataSchemaContextNode.augmentationIdentifierFrom( + augmentationToChild.getKey())); - for (AbstractNodeDataWithSchema nodeDataWithSchema : childsFromAgumentation) { - nodeDataWithSchema.write(writer); + for (AbstractNodeDataWithSchema nodeDataWithSchema : childsFromAgumentation) { + nodeDataWithSchema.write(writer, metaWriter); } writer.endNode(); @@ -216,10 +292,11 @@ public class CompositeNodeDataWithSchema extends AbstractNodeDataWithSchema { * @param child child node * @return augmentation schema */ - private AugmentationSchema findCorrespondingAugment(final DataSchemaNode parent, final DataSchemaNode child) { + private static AugmentationSchemaNode findCorrespondingAugment(final DataSchemaNode parent, + final DataSchemaNode child) { if (parent instanceof AugmentationTarget && !(parent instanceof ChoiceSchemaNode)) { - for (AugmentationSchema augmentation : ((AugmentationTarget) parent).getAvailableAugmentations()) { - DataSchemaNode childInAugmentation = augmentation.getDataChildByName(child.getQName()); + for (AugmentationSchemaNode augmentation : ((AugmentationTarget) parent).getAvailableAugmentations()) { + DataSchemaNode childInAugmentation = augmentation.dataChildByName(child.getQName()); if (childInAugmentation != null) { return augmentation; } @@ -227,16 +304,4 @@ public class CompositeNodeDataWithSchema extends AbstractNodeDataWithSchema { } return null; } - - public static YangInstanceIdentifier.AugmentationIdentifier getNodeIdentifierForAugmentation(final AugmentationSchema schema) { - final Collection qnames = Collections2.transform(schema.getChildNodes(), QNAME_FUNCTION); - return new YangInstanceIdentifier.AugmentationIdentifier(ImmutableSet.copyOf(qnames)); - } - - private static final Function QNAME_FUNCTION = new Function() { - @Override - public QName apply(@Nonnull final DataSchemaNode input) { - return input.getQName(); - } - }; }