X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=yang%2Fyang-data-api%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fyangtools%2Fyang%2Fdata%2Fapi%2Fschema%2Fstream%2FNormalizedNodeWriter.java;h=be22c5091a1a7ef09b9f467b420cbe1e83d53fee;hb=82af449e4ef07d80490e79484d0402b81009541e;hp=a0068c303ace91862932b2aaec02233dd70d5204;hpb=feb866798e33453d6e9255e5f197a64296d33e93;p=yangtools.git diff --git a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/stream/NormalizedNodeWriter.java b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/stream/NormalizedNodeWriter.java index a0068c303a..be22c5091a 100644 --- a/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/stream/NormalizedNodeWriter.java +++ b/yang/yang-data-api/src/main/java/org/opendaylight/yangtools/yang/data/api/schema/stream/NormalizedNodeWriter.java @@ -10,12 +10,18 @@ package org.opendaylight.yangtools.yang.data.api.schema.stream; import static org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter.UNKNOWN_SIZE; import com.google.common.annotations.Beta; +import com.google.common.base.Optional; import com.google.common.base.Preconditions; - +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; import java.io.Closeable; import java.io.Flushable; import java.io.IOException; - +import java.util.Collection; +import java.util.Set; +import javax.xml.stream.XMLStreamReader; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; import org.opendaylight.yangtools.yang.data.api.schema.AnyXmlNode; import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode; import org.opendaylight.yangtools.yang.data.api.schema.ChoiceNode; @@ -26,25 +32,72 @@ import org.opendaylight.yangtools.yang.data.api.schema.LeafSetNode; import org.opendaylight.yangtools.yang.data.api.schema.MapEntryNode; import org.opendaylight.yangtools.yang.data.api.schema.MapNode; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode; +import org.opendaylight.yangtools.yang.data.api.schema.OrderedLeafSetNode; import org.opendaylight.yangtools.yang.data.api.schema.OrderedMapNode; import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode; import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode; +import org.opendaylight.yangtools.yang.data.api.schema.YangModeledAnyXmlNode; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** - * This is an experimental + * This is an experimental iterator over a {@link NormalizedNode}. This is essentially + * the opposite of a {@link XMLStreamReader} -- unlike instantiating an iterator over + * the backing data, this encapsulates a {@link NormalizedNodeStreamWriter} and allows + * us to write multiple nodes. */ @Beta -public final class NormalizedNodeWriter implements Closeable, Flushable { +public class NormalizedNodeWriter implements Closeable, Flushable { private final NormalizedNodeStreamWriter writer; - private NormalizedNodeWriter(final NormalizedNodeStreamWriter writer) { + protected NormalizedNodeWriter(final NormalizedNodeStreamWriter writer) { this.writer = Preconditions.checkNotNull(writer); } + protected final NormalizedNodeStreamWriter getWriter() { + return writer; + } + + /** + * Create a new writer backed by a {@link NormalizedNodeStreamWriter}. + * + * @param writer Back-end writer + * @return A new instance. + */ public static NormalizedNodeWriter forStreamWriter(final NormalizedNodeStreamWriter writer) { - return new NormalizedNodeWriter(writer); + return forStreamWriter(writer, true); } + /** + * Create a new writer backed by a {@link NormalizedNodeStreamWriter}. Unlike the simple {@link #forStreamWriter(NormalizedNodeStreamWriter)} + * method, this allows the caller to switch off RFC6020 XML compliance, providing better + * throughput. The reason is that the XML mapping rules in RFC6020 require the encoding + * to emit leaf nodes which participate in a list's key first and in the order in which + * they are defined in the key. For JSON, this requirement is completely relaxed and leaves + * can be ordered in any way we see fit. The former requires a bit of work: first a lookup + * for each key and then for each emitted node we need to check whether it was already + * emitted. + * + * @param writer Back-end writer + * @param orderKeyLeaves whether the returned instance should be RFC6020 XML compliant. + * @return A new instance. + */ + public static NormalizedNodeWriter forStreamWriter(final NormalizedNodeStreamWriter writer, final boolean orderKeyLeaves) { + if (orderKeyLeaves) { + return new OrderedNormalizedNodeWriter(writer); + } else { + return new NormalizedNodeWriter(writer); + } + } + + /** + * Iterate over the provided {@link NormalizedNode} and emit write + * events to the encapsulated {@link NormalizedNodeStreamWriter}. + * + * @param node Node + * @return NormalizedNodeWriter this + * @throws IOException when thrown from the backing writer. + */ public NormalizedNodeWriter write(final NormalizedNode node) throws IOException { if (wasProcessedAsCompositeNode(node)) { return this; @@ -57,14 +110,47 @@ public final class NormalizedNodeWriter implements Closeable, Flushable { throw new IllegalStateException("It wasn't possible to serialize node " + node); } - private boolean wasProcessAsSimpleNode(final NormalizedNode node) throws IOException { + @Override + public void flush() throws IOException { + writer.flush(); + } + + @Override + public void close() throws IOException { + writer.flush(); + writer.close(); + } + + /** + * Emit a best guess of a hint for a particular set of children. It evaluates the + * iterable to see if the size can be easily gotten to. If it is, we hint at the + * real number of child nodes. Otherwise we emit UNKNOWN_SIZE. + * + * @param children Child nodes + * @return Best estimate of the collection size required to hold all the children. + */ + protected static int childSizeHint(final Iterable children) { + return (children instanceof Collection) ? ((Collection) children).size() : UNKNOWN_SIZE; + } + + protected boolean wasProcessAsSimpleNode(final NormalizedNode node) throws IOException { if (node instanceof LeafSetEntryNode) { final LeafSetEntryNode nodeAsLeafList = (LeafSetEntryNode)node; - writer.leafSetEntryNode(nodeAsLeafList.getValue()); + final QName name = nodeAsLeafList.getIdentifier().getNodeType(); + if (writer instanceof NormalizedNodeStreamAttributeWriter) { + ((NormalizedNodeStreamAttributeWriter) writer).leafSetEntryNode(name, nodeAsLeafList.getValue(), + nodeAsLeafList.getAttributes()); + } else { + writer.leafSetEntryNode(name, nodeAsLeafList.getValue()); + } return true; } else if (node instanceof LeafNode) { final LeafNode nodeAsLeaf = (LeafNode)node; - writer.leafNode(nodeAsLeaf.getIdentifier(), nodeAsLeaf.getValue()); + if (writer instanceof NormalizedNodeStreamAttributeWriter) { + ((NormalizedNodeStreamAttributeWriter) writer).leafNode(nodeAsLeaf.getIdentifier(), nodeAsLeaf.getValue(), nodeAsLeaf.getAttributes()); + } else { + writer.leafNode(nodeAsLeaf.getIdentifier(), nodeAsLeaf.getValue()); + } return true; } else if (node instanceof AnyXmlNode) { final AnyXmlNode anyXmlNode = (AnyXmlNode)node; @@ -75,57 +161,140 @@ public final class NormalizedNodeWriter implements Closeable, Flushable { return false; } - private boolean wasProcessedAsCompositeNode(final NormalizedNode node) throws IOException { - boolean hasDataContainerChild = false; + /** + * Emit events for all children and then emit an endNode() event. + * + * @param children Child iterable + * @return True + * @throws IOException when the writer reports it + */ + protected boolean writeChildren(final Iterable> children) throws IOException { + for (final NormalizedNode child : children) { + write(child); + } + + writer.endNode(); + return true; + } + + protected boolean writeMapEntryNode(final MapEntryNode node) throws IOException { + if (writer instanceof NormalizedNodeStreamAttributeWriter) { + ((NormalizedNodeStreamAttributeWriter) writer) + .startMapEntryNode(node.getIdentifier(), childSizeHint(node.getValue()), node.getAttributes()); + } else { + writer.startMapEntryNode(node.getIdentifier(), childSizeHint(node.getValue())); + } + return writeChildren(node.getValue()); + } + + protected boolean wasProcessedAsCompositeNode(final NormalizedNode node) throws IOException { if (node instanceof ContainerNode) { - writer.startContainerNode(((ContainerNode) node).getIdentifier(), UNKNOWN_SIZE); - hasDataContainerChild = true; - } else if (node instanceof MapEntryNode) { - writer.startMapEntryNode(((MapEntryNode) node).getIdentifier(), UNKNOWN_SIZE); - hasDataContainerChild = true; - } else if (node instanceof UnkeyedListEntryNode) { - writer.startUnkeyedListItem(((UnkeyedListEntryNode) node).getIdentifier(), UNKNOWN_SIZE); - hasDataContainerChild = true; - } else if (node instanceof ChoiceNode) { - writer.startChoiceNode(((ChoiceNode) node).getIdentifier(), UNKNOWN_SIZE); - hasDataContainerChild = true; - } else if (node instanceof AugmentationNode) { - writer.startAugmentationNode(((AugmentationNode) node).getIdentifier()); - hasDataContainerChild = true; - } else if (node instanceof UnkeyedListNode) { - writer.startUnkeyedList(((UnkeyedListNode) node).getIdentifier(), UNKNOWN_SIZE); - hasDataContainerChild = true; - } else if (node instanceof OrderedMapNode) { - writer.startOrderedMapNode(((OrderedMapNode) node).getIdentifier(), UNKNOWN_SIZE); - hasDataContainerChild = true; - } else if (node instanceof MapNode) { - writer.startMapNode(((MapNode) node).getIdentifier(), UNKNOWN_SIZE); - hasDataContainerChild = true; - //covers also OrderedLeafSetNode for which doesn't exist start* method - } else if (node instanceof LeafSetNode) { - writer.startLeafSet(((LeafSetNode) node).getIdentifier(), UNKNOWN_SIZE); - hasDataContainerChild = true; - } - - if (hasDataContainerChild) { - for (NormalizedNode childNode : ((NormalizedNode>>) node).getValue()) { - write(childNode); + final ContainerNode n = (ContainerNode) node; + if (writer instanceof NormalizedNodeStreamAttributeWriter) { + ((NormalizedNodeStreamAttributeWriter) writer).startContainerNode(n.getIdentifier(), childSizeHint(n.getValue()), n.getAttributes()); + } else { + writer.startContainerNode(n.getIdentifier(), childSizeHint(n.getValue())); } - - writer.endNode(); - return true; + return writeChildren(n.getValue()); + } + if (node instanceof YangModeledAnyXmlNode) { + final YangModeledAnyXmlNode n = (YangModeledAnyXmlNode) node; + if (writer instanceof NormalizedNodeStreamAttributeWriter) { + ((NormalizedNodeStreamAttributeWriter) writer).startYangModeledAnyXmlNode(n.getIdentifier(), childSizeHint(n.getValue()), n.getAttributes()); + } else { + writer.startYangModeledAnyXmlNode(n.getIdentifier(), childSizeHint(n.getValue())); + } + return writeChildren(n.getValue()); + } + if (node instanceof MapEntryNode) { + return writeMapEntryNode((MapEntryNode) node); + } + if (node instanceof UnkeyedListEntryNode) { + final UnkeyedListEntryNode n = (UnkeyedListEntryNode) node; + writer.startUnkeyedListItem(n.getIdentifier(), childSizeHint(n.getValue())); + return writeChildren(n.getValue()); + } + if (node instanceof ChoiceNode) { + final ChoiceNode n = (ChoiceNode) node; + writer.startChoiceNode(n.getIdentifier(), childSizeHint(n.getValue())); + return writeChildren(n.getValue()); + } + if (node instanceof AugmentationNode) { + final AugmentationNode n = (AugmentationNode) node; + writer.startAugmentationNode(n.getIdentifier()); + return writeChildren(n.getValue()); + } + if (node instanceof UnkeyedListNode) { + final UnkeyedListNode n = (UnkeyedListNode) node; + writer.startUnkeyedList(n.getIdentifier(), childSizeHint(n.getValue())); + return writeChildren(n.getValue()); + } + if (node instanceof OrderedMapNode) { + final OrderedMapNode n = (OrderedMapNode) node; + writer.startOrderedMapNode(n.getIdentifier(), childSizeHint(n.getValue())); + return writeChildren(n.getValue()); + } + if (node instanceof MapNode) { + final MapNode n = (MapNode) node; + writer.startMapNode(n.getIdentifier(), childSizeHint(n.getValue())); + return writeChildren(n.getValue()); + } + if (node instanceof OrderedLeafSetNode) { + final LeafSetNode n = (LeafSetNode) node; + writer.startOrderedLeafSet(n.getIdentifier(), childSizeHint(n.getValue())); + return writeChildren(n.getValue()); + } + if (node instanceof LeafSetNode) { + final LeafSetNode n = (LeafSetNode) node; + writer.startLeafSet(n.getIdentifier(), childSizeHint(n.getValue())); + return writeChildren(n.getValue()); } - return false; + return false; } - @Override - public void flush() throws IOException { - writer.flush(); - } + private static final class OrderedNormalizedNodeWriter extends NormalizedNodeWriter { + private static final Logger LOG = LoggerFactory.getLogger(OrderedNormalizedNodeWriter.class); - @Override - public void close() throws IOException { - writer.close(); + OrderedNormalizedNodeWriter(final NormalizedNodeStreamWriter writer) { + super(writer); + } + + @Override + protected boolean writeMapEntryNode(final MapEntryNode node) throws IOException { + final NormalizedNodeStreamWriter nnWriter = getWriter(); + if (nnWriter instanceof NormalizedNodeStreamAttributeWriter) { + ((NormalizedNodeStreamAttributeWriter) nnWriter).startMapEntryNode(node.getIdentifier(), childSizeHint(node.getValue()), node.getAttributes()); + } else { + nnWriter.startMapEntryNode(node.getIdentifier(), childSizeHint(node.getValue())); + } + + final Set qnames = node.getIdentifier().getKeyValues().keySet(); + // Write out all the key children + for (final QName qname : qnames) { + final Optional> child = node.getChild(new NodeIdentifier(qname)); + if (child.isPresent()) { + write(child.get()); + } else { + LOG.info("No child for key element {} found", qname); + } + } + + // Write all the rest + return writeChildren(Iterables.filter(node.getValue(), new Predicate>() { + @Override + public boolean apply(final NormalizedNode input) { + if (input instanceof AugmentationNode) { + return true; + } + if (!qnames.contains(input.getNodeType())) { + return true; + } + + LOG.debug("Skipping key child {}", input); + return false; + } + })); + } } }