/* * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved. * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ package org.opendaylight.yangtools.yang.data.impl.schema; import com.google.common.collect.ArrayListMultimap; import java.io.IOException; import java.util.Collection; import java.util.List; import java.util.Objects; import org.opendaylight.yangtools.yang.common.QName; import org.opendaylight.yangtools.yang.data.api.schema.AugmentationNode; import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; 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.api.schema.stream.NormalizedNodeWriter; import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode; import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode; import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; import org.opendaylight.yangtools.yang.model.api.ListSchemaNode; import org.opendaylight.yangtools.yang.model.api.SchemaContext; import org.opendaylight.yangtools.yang.model.api.SchemaNode; import org.opendaylight.yangtools.yang.model.api.SchemaPath; /** * This is an iterator over a {@link NormalizedNode}. Unlike {@link NormalizedNodeWriter}, * this iterates over elements in order as they are defined in .yang file. */ public class SchemaOrderedNormalizedNodeWriter extends NormalizedNodeWriter { private final SchemaContext schemaContext; private final SchemaNode root; private final NormalizedNodeStreamWriter writer; private SchemaNode currentSchemaNode; /** * Create a new writer backed by a {@link NormalizedNodeStreamWriter}. * @param writer Back-end writer * @param schemaContext Schema context * @param path path */ public SchemaOrderedNormalizedNodeWriter(final NormalizedNodeStreamWriter writer, final SchemaContext schemaContext, final SchemaPath path) { super(writer); this.writer = writer; this.schemaContext = schemaContext; this.root = SchemaUtils.findParentSchemaOnPath(schemaContext, path); } @Override public SchemaOrderedNormalizedNodeWriter write(final NormalizedNode<?, ?> node) throws IOException { if (Objects.equals(root, schemaContext)) { currentSchemaNode = schemaContext.getDataChildByName(node.getNodeType()); } else { currentSchemaNode = root; } return write(node, currentSchemaNode); } /** * Iterate over the provided collection and emit write * events to the encapsulated {@link NormalizedNodeStreamWriter}. * * @param nodes nodes * @return NormalizedNodeWriter this * @throws IOException when thrown from the backing writer. */ public SchemaOrderedNormalizedNodeWriter write(final Collection<DataContainerChild<?,?>> nodes) throws IOException { currentSchemaNode = root; if (writeChildren(nodes, currentSchemaNode, false)) { return this; } throw new IllegalStateException("It wasn't possible to serialize nodes " + nodes); } private SchemaOrderedNormalizedNodeWriter write(final NormalizedNode<?, ?> node, final SchemaNode dataSchemaNode) throws IOException { //Set current schemaNode try (SchemaNodeSetter sns = new SchemaNodeSetter(dataSchemaNode)) { if (node == null) { return this; } if (wasProcessedAsCompositeNode(node)) { return this; } if (wasProcessAsSimpleNode(node)) { return this; } } throw new IllegalStateException("It wasn't possible to serialize node " + node); } private void write(final List<NormalizedNode<?, ?>> nodes, final SchemaNode dataSchemaNode) throws IOException { for (NormalizedNode<?, ?> node : nodes) { write(node, dataSchemaNode); } } protected boolean writeChildren(final Iterable<? extends NormalizedNode<?, ?>> children) throws IOException { return writeChildren(children, currentSchemaNode, true); } private boolean writeChildren(final Iterable<? extends NormalizedNode<?, ?>> children, final SchemaNode parentSchemaNode, boolean endParent) throws IOException { //Augmentations cannot be gotten with node.getChild so create our own structure with augmentations resolved ArrayListMultimap<QName, NormalizedNode<?, ?>> qNameToNodes = ArrayListMultimap.create(); for (NormalizedNode<?, ?> child : children) { if (child instanceof AugmentationNode) { qNameToNodes.putAll(resolveAugmentations(child)); } else { qNameToNodes.put(child.getNodeType(), child); } } if (parentSchemaNode instanceof DataNodeContainer) { if (parentSchemaNode instanceof ListSchemaNode && qNameToNodes.containsKey(parentSchemaNode.getQName())) { write(qNameToNodes.get(parentSchemaNode.getQName()), parentSchemaNode); } else { for (DataSchemaNode schemaNode : ((DataNodeContainer) parentSchemaNode).getChildNodes()) { write(qNameToNodes.get(schemaNode.getQName()), schemaNode); } } } else if (parentSchemaNode instanceof ChoiceSchemaNode) { for (ChoiceCaseNode ccNode : ((ChoiceSchemaNode) parentSchemaNode).getCases()) { for (DataSchemaNode dsn : ccNode.getChildNodes()) { if (qNameToNodes.containsKey(dsn.getQName())) { write(qNameToNodes.get(dsn.getQName()), dsn); } } } } else { for (NormalizedNode<?, ?> child : children) { writeLeaf(child); } } if (endParent) { writer.endNode(); } return true; } private SchemaOrderedNormalizedNodeWriter writeLeaf(final NormalizedNode<?, ?> node) throws IOException { if (wasProcessAsSimpleNode(node)) { return this; } throw new IllegalStateException("It wasn't possible to serialize node " + node); } private ArrayListMultimap<QName, NormalizedNode<?, ?>> resolveAugmentations(final NormalizedNode<?, ?> child) { final ArrayListMultimap<QName, NormalizedNode<?, ?>> resolvedAugs = ArrayListMultimap.create(); for (NormalizedNode<?, ?> node : ((AugmentationNode) child).getValue()) { if (node instanceof AugmentationNode) { resolvedAugs.putAll(resolveAugmentations(node)); } else { resolvedAugs.put(node.getNodeType(), node); } } return resolvedAugs; } private class SchemaNodeSetter implements AutoCloseable { private final SchemaNode previousSchemaNode; /** * Sets current schema node new value and store old value for later restore */ public SchemaNodeSetter(final SchemaNode schemaNode) { previousSchemaNode = SchemaOrderedNormalizedNodeWriter.this.currentSchemaNode; SchemaOrderedNormalizedNodeWriter.this.currentSchemaNode = schemaNode; } /** * Restore previous schema node */ @Override public void close() { SchemaOrderedNormalizedNodeWriter.this.currentSchemaNode = previousSchemaNode; } } }