package org.opendaylight.controller.cluster.datastore.node.utils.transformer;
import static com.google.common.base.Preconditions.checkState;
+import static com.google.common.base.Verify.verify;
import static java.util.Objects.requireNonNull;
+import java.io.IOException;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.NoSuchElementException;
import java.util.Optional;
import javax.xml.transform.dom.DOMSource;
-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;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeWithValue;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
-import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode;
-import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
-import org.opendaylight.yangtools.yang.data.impl.schema.Builders;
-import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
+import org.opendaylight.yangtools.yang.data.impl.schema.ReusableImmutableNormalizedNodeStreamWriter;
import org.opendaylight.yangtools.yang.data.util.DataSchemaContextNode;
import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
CLOSED;
}
+ @FunctionalInterface
+ interface WriterMethod<T extends PathArgument> {
+
+ void apply(ReusableImmutableNormalizedNodeStreamWriter writer, T name) throws IOException;
+ }
+
+ @FunctionalInterface
+ interface SizedWriterMethod<T extends PathArgument> {
+
+ void apply(ReusableImmutableNormalizedNodeStreamWriter writer, T name, int childSizeHint) throws IOException;
+ }
+
private static final Logger LOG = LoggerFactory.getLogger(AbstractNormalizedNodePruner.class);
- private final Deque<NormalizedNodeBuilderWrapper> stack = new ArrayDeque<>();
+ private final Deque<DataSchemaContextNode<?>> stack = new ArrayDeque<>();
+ private final ReusableImmutableNormalizedNodeStreamWriter delegate =
+ ReusableImmutableNormalizedNodeStreamWriter.create();
private final DataSchemaContextTree tree;
private DataSchemaContextNode<?> nodePathSchemaNode;
+ private NormalizedNode<?, ?> normalizedNode;
private State state = State.UNITIALIZED;
-
- // FIXME: package-private to support unguarded NormalizedNodePruner access
- NormalizedNode<?, ?> normalizedNode;
+ private int unknown;
AbstractNormalizedNodePruner(final DataSchemaContextTree tree) {
this.tree = requireNonNull(tree);
final void initialize(final YangInstanceIdentifier nodePath) {
nodePathSchemaNode = tree.findChild(nodePath).orElse(null);
+ unknown = 0;
normalizedNode = null;
stack.clear();
+ delegate.reset();
state = State.OPEN;
}
- @SuppressWarnings("unchecked")
@Override
- public void leafNode(final NodeIdentifier nodeIdentifier, final Object value) {
- checkNotSealed();
-
- NormalizedNodeBuilderWrapper parent = stack.peek();
- LeafNode<Object> leafNode = Builders.leafBuilder().withNodeIdentifier(nodeIdentifier).withValue(value).build();
- if (parent != null) {
- if (hasValidSchema(nodeIdentifier.getNodeType(), parent)) {
- parent.builder().addChild(leafNode);
- }
- } else {
- // If there's no parent node then this is a stand alone LeafNode.
- if (nodePathSchemaNode != null) {
- this.normalizedNode = leafNode;
- }
-
- state = State.CLOSED;
- }
+ public final void startLeafNode(final NodeIdentifier name) throws IOException {
+ enter(ReusableImmutableNormalizedNodeStreamWriter::startLeafNode, name);
}
@Override
- public void startLeafSet(final NodeIdentifier nodeIdentifier, final int count) {
- addBuilder(Builders.leafSetBuilder().withNodeIdentifier(nodeIdentifier), nodeIdentifier);
+ public final void startLeafSet(final NodeIdentifier name, final int childSizeHint) throws IOException {
+ enter(ReusableImmutableNormalizedNodeStreamWriter::startLeafSet, name, childSizeHint);
}
@Override
- public void startOrderedLeafSet(final NodeIdentifier nodeIdentifier, final int str) {
- addBuilder(Builders.orderedLeafSetBuilder().withNodeIdentifier(nodeIdentifier), nodeIdentifier);
+ public final void startOrderedLeafSet(final NodeIdentifier name, final int childSizeHint) throws IOException {
+ enter(ReusableImmutableNormalizedNodeStreamWriter::startOrderedLeafSet, name, childSizeHint);
}
- @SuppressWarnings("unchecked")
@Override
- public void leafSetEntryNode(final QName name, final Object value) {
- checkNotSealed();
-
- NormalizedNodeBuilderWrapper parent = stack.peek();
- if (parent != null) {
- if (hasValidSchema(name, parent)) {
- parent.builder().addChild(Builders.leafSetEntryBuilder().withValue(value)
- .withNodeIdentifier(new NodeWithValue<>(parent.nodeType(), value))
- .build());
- }
- } else {
- // If there's no parent LeafSetNode then this is a stand alone
- // LeafSetEntryNode.
- if (nodePathSchemaNode != null) {
- this.normalizedNode = Builders.leafSetEntryBuilder().withValue(value).withNodeIdentifier(
- new NodeWithValue<>(name, value)).build();
- }
-
- state = State.CLOSED;
- }
+ public void startLeafSetEntryNode(final NodeWithValue<?> name) throws IOException {
+ enter(ReusableImmutableNormalizedNodeStreamWriter::startLeafSetEntryNode, name);
}
@Override
- public void startContainerNode(final NodeIdentifier nodeIdentifier, final int count) {
- addBuilder(Builders.containerBuilder().withNodeIdentifier(nodeIdentifier), nodeIdentifier);
+ public final void startContainerNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
+ enter(ReusableImmutableNormalizedNodeStreamWriter::startContainerNode, name, childSizeHint);
}
@Override
- public void startYangModeledAnyXmlNode(final NodeIdentifier nodeIdentifier, final int count) {
+ public final void startYangModeledAnyXmlNode(final NodeIdentifier nodeIdentifier, final int count) {
+ // FIXME: implement this
throw new UnsupportedOperationException("Not implemented yet");
}
@Override
- public void startUnkeyedList(final NodeIdentifier nodeIdentifier, final int count) {
- addBuilder(Builders.unkeyedListBuilder().withNodeIdentifier(nodeIdentifier), nodeIdentifier);
+ public final void startUnkeyedList(final NodeIdentifier name, final int childSizeHint) throws IOException {
+ enter(ReusableImmutableNormalizedNodeStreamWriter::startUnkeyedList, name, childSizeHint);
}
@Override
- public void startUnkeyedListItem(final NodeIdentifier nodeIdentifier, final int count) {
- addBuilder(Builders.unkeyedListEntryBuilder().withNodeIdentifier(nodeIdentifier), nodeIdentifier);
+ public final void startUnkeyedListItem(final NodeIdentifier name, final int childSizeHint) throws IOException {
+ enter(ReusableImmutableNormalizedNodeStreamWriter::startUnkeyedListItem, name, childSizeHint);
}
@Override
- public void startMapNode(final NodeIdentifier nodeIdentifier, final int count) {
- addBuilder(Builders.mapBuilder().withNodeIdentifier(nodeIdentifier), nodeIdentifier);
+ public final void startMapNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
+ enter(ReusableImmutableNormalizedNodeStreamWriter::startMapNode, name, childSizeHint);
}
@Override
- public void startMapEntryNode(final NodeIdentifierWithPredicates nodeIdentifierWithPredicates, final int count) {
- addBuilder(Builders.mapEntryBuilder().withNodeIdentifier(nodeIdentifierWithPredicates),
- nodeIdentifierWithPredicates);
+ public void startMapEntryNode(final NodeIdentifierWithPredicates identifier, final int childSizeHint)
+ throws IOException {
+ enter(ReusableImmutableNormalizedNodeStreamWriter::startMapEntryNode, identifier, childSizeHint);
}
@Override
- public void startOrderedMapNode(final NodeIdentifier nodeIdentifier, final int count) {
- addBuilder(Builders.orderedMapBuilder().withNodeIdentifier(nodeIdentifier), nodeIdentifier);
+ public final void startOrderedMapNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
+ enter(ReusableImmutableNormalizedNodeStreamWriter::startOrderedMapNode, name, childSizeHint);
}
@Override
- public void startChoiceNode(final NodeIdentifier nodeIdentifier, final int count) {
- addBuilder(Builders.choiceBuilder().withNodeIdentifier(nodeIdentifier), nodeIdentifier);
+ public final void startChoiceNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
+ enter(ReusableImmutableNormalizedNodeStreamWriter::startChoiceNode, name, childSizeHint);
}
@Override
- public void startAugmentationNode(final AugmentationIdentifier augmentationIdentifier) {
- addBuilder(Builders.augmentationBuilder().withNodeIdentifier(augmentationIdentifier), augmentationIdentifier);
+ public final void startAugmentationNode(final AugmentationIdentifier identifier) throws IOException {
+ enter(ReusableImmutableNormalizedNodeStreamWriter::startAugmentationNode, identifier);
}
- @SuppressWarnings("unchecked")
@Override
- public void anyxmlNode(final NodeIdentifier nodeIdentifier, final Object value) {
- checkNotSealed();
+ public final boolean startAnyxmlNode(final NodeIdentifier name, final Class<?> objectModel) throws IOException {
+ if (enter(name)) {
+ verify(delegate.startAnyxmlNode(name, objectModel),
+ "Unexpected failure to stream DOMSource node %s model %s", name, objectModel);
+ }
+ return true;
+ }
- NormalizedNodeBuilderWrapper parent = stack.peek();
- AnyXmlNode anyXmlNode = Builders.anyXmlBuilder().withNodeIdentifier(nodeIdentifier).withValue((DOMSource) value)
- .build();
- if (parent != null) {
- if (hasValidSchema(nodeIdentifier.getNodeType(), parent)) {
- parent.builder().addChild(anyXmlNode);
- }
- } else {
- // If there's no parent node then this is a stand alone AnyXmlNode.
- if (nodePathSchemaNode != null) {
- this.normalizedNode = anyXmlNode;
- }
+ @Override
+ public final boolean startAnydataNode(final NodeIdentifier name, final Class<?> objectModel) throws IOException {
+ // FIXME: we do not support anydata nodes yet
+ return false;
+ }
- state = State.CLOSED;
+ @Override
+ public final void domSourceValue(final DOMSource value) throws IOException {
+ checkNotSealed();
+ if (unknown == 0) {
+ delegate.domSourceValue(value);
}
}
- @SuppressWarnings("unchecked")
@Override
- public void endNode() {
+ public final void scalarValue(final Object value) throws IOException {
checkNotSealed();
-
- final NormalizedNodeBuilderWrapper child;
- try {
- child = stack.pop();
- } catch (NoSuchElementException e) {
- throw new IllegalStateException("endNode called on an empty stack", e);
+ if (unknown == 0) {
+ delegate.scalarValue(translateScalar(currentSchema(), value));
}
+ }
- if (child.getSchema() == null) {
- LOG.debug("Schema not found for {}", child.identifier());
- if (stack.isEmpty()) {
- normalizedNode = null;
- state = State.CLOSED;
+ Object translateScalar(final DataSchemaContextNode<?> context, final Object value) throws IOException {
+ // Default is pass-through
+ return value;
+ }
+
+ @Override
+ public final void endNode() throws IOException {
+ checkNotSealed();
+
+ if (unknown == 0) {
+ try {
+ stack.pop();
+ } catch (NoSuchElementException e) {
+ throw new IllegalStateException("endNode called on an empty stack", e);
+ }
+ delegate.endNode();
+ } else {
+ unknown--;
+ if (unknown != 0) {
+ // Still at unknown, do not attempt to create result
+ return;
}
- return;
}
- final NormalizedNode<?, ?> newNode = child.builder().build();
- final NormalizedNodeBuilderWrapper parent = stack.peek();
- if (parent == null) {
- normalizedNode = newNode;
+ if (stack.isEmpty()) {
+ normalizedNode = delegate.getResult();
state = State.CLOSED;
- } else {
- parent.builder().addChild(newNode);
}
}
@Override
- public void close() {
+ public final void close() throws IOException {
state = State.CLOSED;
stack.clear();
+ delegate.close();
}
@Override
- public void flush() {
- // No-op
+ public final void flush() throws IOException {
+ delegate.flush();
}
/**
checkState(state == State.OPEN, "Illegal operation in state %s", state);
}
- private static boolean hasValidSchema(final QName name, final NormalizedNodeBuilderWrapper parent) {
- final DataSchemaContextNode<?> parentSchema = parent.getSchema();
- final boolean valid = parentSchema != null && parentSchema.getChild(name) != null;
- if (!valid) {
+ private boolean enter(final PathArgument name) {
+ checkNotSealed();
+
+ if (unknown != 0) {
+ LOG.debug("Skipping child {} in unknown subtree", name);
+ unknown++;
+ return false;
+ }
+
+ final DataSchemaContextNode<?> schema;
+ final DataSchemaContextNode<?> parent = currentSchema();
+ if (parent != null) {
+ schema = parent.getChild(name);
+ } else {
+ schema = nodePathSchemaNode;
+ }
+
+ if (schema == null) {
LOG.debug("Schema not found for {}", name);
+ unknown = 1;
+ return false;
}
- return valid;
+ stack.push(schema);
+ final DataSchemaNode dataSchema = schema.getDataSchemaNode();
+ if (dataSchema != null) {
+ delegate.nextDataSchemaNode(dataSchema);
+ }
+ return true;
}
- private NormalizedNodeBuilderWrapper addBuilder(final NormalizedNodeContainerBuilder<?, ?, ?, ?> builder,
- final PathArgument identifier) {
- checkNotSealed();
+ final <A extends PathArgument> void enter(final WriterMethod<A> method, final A name) throws IOException {
+ if (enter(name)) {
+ method.apply(delegate, name);
+ }
+ }
- final DataSchemaContextNode<?> schemaNode;
- final NormalizedNodeBuilderWrapper parent = stack.peek();
- if (parent != null) {
- final DataSchemaContextNode<?> parentSchema = parent.getSchema();
- schemaNode = parentSchema == null ? null : parentSchema.getChild(identifier);
- } else {
- schemaNode = nodePathSchemaNode;
+ final <A extends PathArgument> void enter(final SizedWriterMethod<A> method, final A name, final int size)
+ throws IOException {
+ if (enter(name)) {
+ method.apply(delegate, name, size);
}
+ }
- NormalizedNodeBuilderWrapper wrapper = new NormalizedNodeBuilderWrapper(builder, identifier, schemaNode);
- stack.push(wrapper);
- return wrapper;
+ final DataSchemaContextNode<?> currentSchema() {
+ return stack.peek();
}
}