<module>yang-data-util</module>
<module>yang-data-impl</module>
<module>yang-data-operations</module>
+ <module>yang-data-codec-gson</module>
<module>yang-model-api</module>
<module>yang-maven-plugin</module>
<module>yang-maven-plugin-it</module>
--- /dev/null
+package org.opendaylight.yangtools.yang.data.api.schema.stream;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
+
+import java.io.IOException;
+
+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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Beta
+public class LoggingNormalizedNodeStreamWriter implements NormalizedNodeStreamWriter {
+ private static final Logger LOG = LoggerFactory.getLogger(LoggingNormalizedNodeStreamWriter.class);
+ private static final int DEFAULT_INDENT_SIZE = 2;
+ private final int indentSize = DEFAULT_INDENT_SIZE;
+ private int currentIndent = 0;
+
+ private String ind() {
+ return Strings.repeat(" ", currentIndent);
+ }
+
+ private void decIndent() {
+ Preconditions.checkState(currentIndent >= 0, "Unexpected indentation %s", currentIndent);
+ currentIndent -= indentSize;
+ }
+
+ private void incIndent() {
+ currentIndent += indentSize;
+ }
+
+ @Override
+ public void startUnkeyedListItem(final NodeIdentifier name, final int childSizeHint) throws IllegalStateException {
+ LOG.debug("{}{}[](no key)", ind(), name);
+ incIndent();
+ }
+
+ @Override
+ public void startUnkeyedList(final NodeIdentifier name, final int childSizeHint) throws IllegalArgumentException {
+ LOG.debug("{}{}(no key)", ind(), name);
+ incIndent();
+ }
+
+ @Override
+ public void startOrderedMapNode(final NodeIdentifier name, final int childSizeHint) throws IllegalArgumentException {
+
+ }
+
+ @Override
+ public void startMapNode(final NodeIdentifier name, final int childSizeHint) throws IllegalArgumentException {
+ LOG.debug("{}{}(key)", ind(), name);
+ incIndent();
+ }
+
+ @Override
+ public void startMapEntryNode(final NodeIdentifierWithPredicates identifier, final int childSizeHint)
+ throws IllegalArgumentException {
+ LOG.debug("{}{}[](key)", ind(), identifier);
+ incIndent();
+ }
+
+ @Override
+ public void startLeafSet(final NodeIdentifier name, final int childSizeHint) throws IllegalArgumentException {
+ LOG.debug("{}{}(leaf-list)", ind(), name);
+ incIndent();
+ }
+
+ @Override
+ public void startContainerNode(final NodeIdentifier name, final int childSizeHint) throws IllegalArgumentException {
+ LOG.debug("{}{}(container)", ind(), name);
+ incIndent();
+ }
+
+ @Override
+ public void startChoiceNode(final NodeIdentifier name, final int childSizeHint) throws IllegalArgumentException {
+ LOG.debug("{}{}(choice)", ind(), name);
+ incIndent();
+ }
+
+ @Override
+ public void startAugmentationNode(final AugmentationIdentifier identifier) throws IllegalArgumentException {
+ LOG.debug("{}{}(augmentation)", ind(), identifier);
+ incIndent();
+ }
+
+ @Override
+ public void leafSetEntryNode(final Object value) throws IllegalArgumentException {
+ LOG.debug("{}{}({}) ", ind(), value, value.getClass().getSimpleName());
+ }
+
+ @Override
+ public void leafNode(final NodeIdentifier name, final Object value) throws IllegalArgumentException {
+ LOG.debug("{}{}(leaf({}))=", ind(), name, value.getClass().getSimpleName(), value);
+ }
+
+ @Override
+ public void endNode() throws IllegalStateException {
+ decIndent();
+ LOG.debug("{}(end)", ind());
+ }
+
+ @Override
+ public void anyxmlNode(final NodeIdentifier name, final Object value) throws IllegalArgumentException {
+ LOG.debug("{}{}(anyxml)=", ind(), name, value);
+ }
+
+ @Override
+ public void flush() throws IOException {
+ LOG.trace("<<FLUSH>>");
+ }
+
+ @Override
+ public void close() throws IOException {
+ LOG.debug("<<END-OF-STREAM>>");
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2014 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.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.Preconditions;
+
+import java.io.Closeable;
+import java.io.Flushable;
+import java.io.IOException;
+
+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;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafNode;
+import org.opendaylight.yangtools.yang.data.api.schema.LeafSetEntryNode;
+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.OrderedMapNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListEntryNode;
+import org.opendaylight.yangtools.yang.data.api.schema.UnkeyedListNode;
+
+/**
+ * This is an experimental
+ */
+@Beta
+public final class NormalizedNodeWriter implements Closeable, Flushable {
+ private final NormalizedNodeStreamWriter writer;
+
+ private NormalizedNodeWriter(final NormalizedNodeStreamWriter writer) {
+ this.writer = Preconditions.checkNotNull(writer);
+ }
+
+ public static NormalizedNodeWriter forStreamWriter(final NormalizedNodeStreamWriter writer) {
+ return new NormalizedNodeWriter(writer);
+ }
+
+ public NormalizedNodeWriter write(final NormalizedNode<?, ?> node) throws IOException {
+ if (wasProcessedAsCompositeNode(node)) {
+ return this;
+ }
+
+ if (wasProcessAsSimpleNode(node)) {
+ return this;
+ }
+
+ throw new IllegalStateException("It wasn't possible to serialize node " + node);
+ }
+
+ private boolean wasProcessAsSimpleNode(final NormalizedNode<?, ?> node) throws IOException {
+ if (node instanceof LeafSetEntryNode) {
+ final LeafSetEntryNode<?> nodeAsLeafList = (LeafSetEntryNode<?>)node;
+ writer.leafSetEntryNode(nodeAsLeafList.getValue());
+ return true;
+ } else if (node instanceof LeafNode) {
+ final LeafNode<?> nodeAsLeaf = (LeafNode<?>)node;
+ writer.leafNode(nodeAsLeaf.getIdentifier(), nodeAsLeaf.getValue());
+ return true;
+ } else if (node instanceof AnyXmlNode) {
+ final AnyXmlNode anyXmlNode = (AnyXmlNode)node;
+ writer.anyxmlNode(anyXmlNode.getIdentifier(), anyXmlNode.getValue());
+ return true;
+ }
+
+ return false;
+ }
+
+ private boolean wasProcessedAsCompositeNode(final NormalizedNode<?, ?> node) throws IOException {
+ boolean hasDataContainerChild = false;
+ 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<?, Iterable<NormalizedNode<?, ?>>>) node).getValue()) {
+ write(childNode);
+ }
+
+ writer.endNode();
+ return true;
+ }
+ return false;
+
+ }
+
+ @Override
+ public void flush() throws IOException {
+ writer.flush();
+ }
+
+ @Override
+ public void close() throws IOException {
+ writer.close();
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- vi: set et smarttab sw=4 tabstop=4: -->
+<!--
+ Copyright (c) 2013 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
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yangtools-parent</artifactId>
+ <version>0.6.2-SNAPSHOT</version>
+ <relativePath>/../../common/parent/pom.xml</relativePath>
+ </parent>
+
+ <modelVersion>4.0.0</modelVersion>
+ <artifactId>yang-data-codec-gson</artifactId>
+ <name>${project.artifactId}</name>
+ <description>${project.artifactId}</description>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-data-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.google.code.gson</groupId>
+ <artifactId>gson</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-data-impl</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.opendaylight.yangtools</groupId>
+ <artifactId>yang-parser-impl</artifactId>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+</project>
--- /dev/null
+/*
+ * Copyright (c) 2014 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.codec.gson;
+
+import java.io.IOException;
+
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+abstract class AbstractNodeDataWithSchema {
+
+ private final DataSchemaNode schema;
+
+ public AbstractNodeDataWithSchema(final DataSchemaNode schema) {
+ this.schema = schema;
+ }
+
+ public final DataSchemaNode getSchema() {
+ return schema;
+ }
+
+ protected abstract void writeToStream(final NormalizedNodeStreamWriter nnStreamWriter) throws IOException;
+
+ protected NodeIdentifier provideNodeIdentifier() {
+ return new NodeIdentifier(schema.getQName());
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((schema == null) ? 0 : schema.hashCode());
+ return result;
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ AbstractNodeDataWithSchema other = (AbstractNodeDataWithSchema) obj;
+ if (schema == null) {
+ if (other.schema != null) {
+ return false;
+ }
+ } else if (!schema.equals(other.schema)) {
+ return false;
+ }
+
+ return true;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 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.codec.gson;
+
+import java.io.IOException;
+
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+class AnyXmlNodeDataWithSchema extends SimpleNodeDataWithSchema {
+
+ public AnyXmlNodeDataWithSchema(final DataSchemaNode dataSchemaNode) {
+ super(dataSchemaNode);
+ }
+
+ @Override
+ protected void writeToStream(final NormalizedNodeStreamWriter nnStreamWriter) throws IOException {
+// FIXME: should be changed according to format of value
+ nnStreamWriter.anyxmlNode(provideNodeIdentifier(), getValue());
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 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.codec.gson;
+
+import java.io.IOException;
+
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
+
+class CaseNodeDataWithSchema extends CompositeNodeDataWithSchema {
+
+ public CaseNodeDataWithSchema(final ChoiceCaseNode schema) {
+ super(schema);
+ }
+
+ @Override
+ protected void writeToStream(final NormalizedNodeStreamWriter nnStreamWriter) throws IOException {
+ super.writeToStream(nnStreamWriter);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 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.codec.gson;
+
+import static org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter.UNKNOWN_SIZE;
+
+import java.io.IOException;
+
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+/**
+ *
+ * childs - empty augment - only one element can be
+ *
+ */
+class ChoiceNodeDataWithSchema extends CompositeNodeDataWithSchema {
+
+ private CaseNodeDataWithSchema caseNodeDataWithSchema;
+
+ public ChoiceNodeDataWithSchema(final ChoiceNode schema) {
+ super(schema);
+ }
+
+ @Override
+ public CompositeNodeDataWithSchema addCompositeChild(final DataSchemaNode schema) {
+ CaseNodeDataWithSchema newChild = new CaseNodeDataWithSchema((ChoiceCaseNode) schema);
+ caseNodeDataWithSchema = newChild;
+ addCompositeChild(newChild);
+ return newChild;
+ }
+
+ public CaseNodeDataWithSchema getCase() {
+ return caseNodeDataWithSchema;
+ }
+
+ @Override
+ protected void writeToStream(final NormalizedNodeStreamWriter nnStreamWriter) throws IOException {
+ nnStreamWriter.startChoiceNode(provideNodeIdentifier(), UNKNOWN_SIZE);
+ super.writeToStream(nnStreamWriter);
+ nnStreamWriter.endNode();
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 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.codec.gson;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
+import org.opendaylight.yangtools.yang.model.api.AugmentationTarget;
+import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+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;
+
+class CompositeNodeDataWithSchema extends AbstractNodeDataWithSchema {
+
+ /**
+ * nodes which were added to schema via augmentation and are present in data input
+ */
+ protected Map<AugmentationSchema, List<AbstractNodeDataWithSchema>> augmentationsToChild = new HashMap<>();
+
+ /**
+ * remaining data nodes (which aren't added via augment). Every of them should have the same QName
+ */
+ protected List<AbstractNodeDataWithSchema> childs = new ArrayList<>();
+
+ public CompositeNodeDataWithSchema(final DataSchemaNode schema) {
+ super(schema);
+ }
+
+ public AbstractNodeDataWithSchema addSimpleChild(final DataSchemaNode schema) {
+ SimpleNodeDataWithSchema newChild = null;
+ if (schema instanceof LeafSchemaNode) {
+ newChild = new LeafNodeDataWithSchema(schema);
+ } else if (schema instanceof AnyXmlSchemaNode) {
+ newChild = new AnyXmlNodeDataWithSchema(schema);
+ }
+
+ if (newChild != null) {
+
+ AugmentationSchema augSchema = null;
+ if (schema.isAugmenting()) {
+ augSchema = findCorrespondingAugment(getSchema(), schema);
+ }
+ if (augSchema != null) {
+ addChildToAugmentation(augSchema, newChild);
+ } else {
+ addChild(newChild);
+ }
+ return newChild;
+ }
+ return null;
+ }
+
+ private void addChildToAugmentation(final AugmentationSchema augSchema, final AbstractNodeDataWithSchema newChild) {
+ List<AbstractNodeDataWithSchema> childsInAugment = augmentationsToChild.get(augSchema);
+ if (childsInAugment == null) {
+ childsInAugment = new ArrayList<>();
+ augmentationsToChild.put(augSchema, childsInAugment);
+ }
+ childsInAugment.add(newChild);
+ }
+
+ public AbstractNodeDataWithSchema addChild(final Deque<DataSchemaNode> schemas) {
+ if (schemas.size() == 1) {
+ final DataSchemaNode childDataSchemaNode = schemas.pop();
+ return addChild(childDataSchemaNode);
+ } else {
+ DataSchemaNode choiceCandidate = schemas.pop();
+ DataSchemaNode caseCandidate = schemas.pop();
+ ChoiceNode choiceNode = null;
+ ChoiceCaseNode caseNode = null;
+ if (choiceCandidate instanceof ChoiceNode) {
+ choiceNode = (ChoiceNode) choiceCandidate;
+ } else {
+ throw new IllegalArgumentException("Awaited node of type ChoiceNode but was "
+ + choiceCandidate.getClass().getSimpleName());
+ }
+
+ if (caseCandidate instanceof ChoiceCaseNode) {
+ caseNode = (ChoiceCaseNode) caseCandidate;
+ } else {
+ throw new IllegalArgumentException("Awaited node of type ChoiceCaseNode but was "
+ + caseCandidate.getClass().getSimpleName());
+ }
+
+ AugmentationSchema augSchema = null;
+ if (choiceCandidate.isAugmenting()) {
+ augSchema = findCorrespondingAugment(getSchema(), choiceCandidate);
+ }
+
+ // looking for existing choice
+ List<AbstractNodeDataWithSchema> childNodes = Collections.emptyList();
+ if (augSchema != null) {
+ childNodes = augmentationsToChild.get(augSchema);
+ } else {
+ childNodes = childs;
+ }
+
+ CompositeNodeDataWithSchema caseNodeDataWithSchema = findChoice(childNodes, choiceCandidate, caseCandidate);
+ if (caseNodeDataWithSchema == null) {
+ ChoiceNodeDataWithSchema choiceNodeDataWithSchema = new ChoiceNodeDataWithSchema(choiceNode);
+ addChild(choiceNodeDataWithSchema);
+ caseNodeDataWithSchema = choiceNodeDataWithSchema.addCompositeChild(caseNode);
+ }
+
+ return caseNodeDataWithSchema.addChild(schemas);
+ }
+
+ }
+
+ private CaseNodeDataWithSchema findChoice(final List<AbstractNodeDataWithSchema> childNodes, final DataSchemaNode choiceCandidate,
+ final DataSchemaNode caseCandidate) {
+ if (childNodes == null) {
+ return null;
+ }
+ for (AbstractNodeDataWithSchema nodeDataWithSchema : childNodes) {
+ if (nodeDataWithSchema instanceof ChoiceNodeDataWithSchema
+ && nodeDataWithSchema.getSchema().getQName().equals(choiceCandidate.getQName())) {
+ CaseNodeDataWithSchema casePrevious = ((ChoiceNodeDataWithSchema) nodeDataWithSchema).getCase();
+ if (casePrevious.getSchema().getQName() != caseCandidate.getQName()) {
+ throw new IllegalArgumentException("Data from case " + caseCandidate.getQName()
+ + " are specified but other data from case " + casePrevious.getSchema().getQName()
+ + " were specified erlier. Data aren't from the same case.");
+ }
+ return casePrevious;
+ }
+ }
+ return null;
+ }
+
+ public AbstractNodeDataWithSchema addCompositeChild(final DataSchemaNode schema) {
+ CompositeNodeDataWithSchema newChild;
+ if (schema instanceof ListSchemaNode) {
+ newChild = new ListNodeDataWithSchema(schema);
+ } else if (schema instanceof LeafListSchemaNode) {
+ newChild = new LeafListNodeDataWithSchema(schema);
+ } else if (schema instanceof ContainerSchemaNode) {
+ newChild = new ContainerNodeDataWithSchema(schema);
+ } else {
+ newChild = new CompositeNodeDataWithSchema(schema);
+ }
+ addCompositeChild(newChild);
+ return newChild;
+ }
+
+ public void addCompositeChild(final CompositeNodeDataWithSchema newChild) {
+ AugmentationSchema augSchema = findCorrespondingAugment(getSchema(), newChild.getSchema());
+ if (augSchema != null) {
+ addChildToAugmentation(augSchema, newChild);
+ } else {
+ addChild(newChild);
+ }
+ }
+
+ private AbstractNodeDataWithSchema addChild(final DataSchemaNode schema) {
+ AbstractNodeDataWithSchema newChild = addSimpleChild(schema);
+ return newChild == null ? addCompositeChild(schema) : newChild;
+ }
+
+ public void addChild(final AbstractNodeDataWithSchema newChild) {
+ childs.add(newChild);
+ }
+
+ /**
+ * Tries to find in {@code parent} which is dealed as augmentation target node with QName as {@code child}. If such
+ * node is found then it is returned, else null.
+ */
+ protected AugmentationSchema findCorrespondingAugment(final DataSchemaNode parent, final DataSchemaNode child) {
+ if (parent instanceof AugmentationTarget) {
+ for (AugmentationSchema augmentation : ((AugmentationTarget) parent).getAvailableAugmentations()) {
+ DataSchemaNode childInAugmentation = augmentation.getDataChildByName(child.getQName());
+ if (childInAugmentation != null) {
+ return augmentation;
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ protected void writeToStream(final NormalizedNodeStreamWriter nnStreamWriter) throws IOException {
+ for (AbstractNodeDataWithSchema child : childs) {
+ child.writeToStream(nnStreamWriter);
+ }
+ for (Entry<AugmentationSchema, List<AbstractNodeDataWithSchema>> augmentationToChild : augmentationsToChild.entrySet()) {
+
+ final List<AbstractNodeDataWithSchema> childsFromAgumentation = augmentationToChild.getValue();
+
+ if (!childsFromAgumentation.isEmpty()) {
+ nnStreamWriter.startAugmentationNode(toAugmentationIdentifier(augmentationToChild));
+
+ for (AbstractNodeDataWithSchema nodeDataWithSchema : childsFromAgumentation) {
+ nodeDataWithSchema.writeToStream(nnStreamWriter);
+ }
+
+ nnStreamWriter.endNode();
+ }
+ }
+ }
+
+ private static AugmentationIdentifier toAugmentationIdentifier(
+ final Entry<AugmentationSchema, List<AbstractNodeDataWithSchema>> augmentationToChild) {
+ Collection<DataSchemaNode> nodes = augmentationToChild.getKey().getChildNodes();
+ Set<QName> nodesQNames = new HashSet<>();
+ for (DataSchemaNode node : nodes) {
+ nodesQNames.add(node.getQName());
+ }
+
+ return new AugmentationIdentifier(nodesQNames);
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 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.codec.gson;
+
+import static org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter.UNKNOWN_SIZE;
+
+import java.io.IOException;
+
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+class ContainerNodeDataWithSchema extends CompositeNodeDataWithSchema {
+
+ public ContainerNodeDataWithSchema(final DataSchemaNode schema) {
+ super(schema);
+ }
+
+ @Override
+ protected void writeToStream(final NormalizedNodeStreamWriter nnStreamWriter) throws IOException {
+ nnStreamWriter.startContainerNode(provideNodeIdentifier(), UNKNOWN_SIZE);
+ super.writeToStream(nnStreamWriter);
+ nnStreamWriter.endNode();
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 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.codec.gson;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Strings;
+import com.google.gson.stream.JsonWriter;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.net.URI;
+import java.util.ArrayDeque;
+import java.util.Deque;
+
+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.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * This implementation will create JSON output as output stream.
+ *
+ * Values of leaf and leaf-list are NOT translated according to codecs.
+ *
+ * FIXME: rewrite this in terms of {@link JsonWriter}.
+ */
+public class JSONNormalizedNodeStreamWriter implements NormalizedNodeStreamWriter {
+
+ private static enum NodeType {
+ OBJECT,
+ LIST,
+ OTHER,
+ }
+
+ private static class TypeInfo {
+ private boolean hasAtLeastOneChild = false;
+ private final NodeType type;
+ private final URI uri;
+
+ public TypeInfo(final NodeType type, final URI uri) {
+ this.type = type;
+ this.uri = uri;
+ }
+
+ public void setHasAtLeastOneChild(final boolean hasChildren) {
+ this.hasAtLeastOneChild = hasChildren;
+ }
+
+ public NodeType getType() {
+ return type;
+ }
+
+ public URI getNamespace() {
+ return uri;
+ }
+
+ public boolean hasAtLeastOneChild() {
+ return hasAtLeastOneChild;
+ }
+ }
+
+ private final Deque<TypeInfo> stack = new ArrayDeque<>();
+ private final SchemaContext schemaContext;
+ private final Writer writer;
+ private final String indent;
+
+ private URI currentNamespace = null;
+ private int currentDepth = 0;
+
+ private JSONNormalizedNodeStreamWriter(final SchemaContext schemaContext,
+ final Writer writer, final int indentSize) {
+ this.schemaContext = Preconditions.checkNotNull(schemaContext);
+ this.writer = Preconditions.checkNotNull(writer);
+
+ Preconditions.checkArgument(indentSize >= 0, "Indent size must be non-negative");
+
+ if (indentSize != 0) {
+ indent = Strings.repeat(" ", indentSize);
+ } else {
+ indent = null;
+ }
+ }
+
+ /**
+ * Create a new stream writer, which writes to the specified {@link Writer}.
+ *
+ * @param schemaContext Schema context
+ * @param writer Output writer
+ * @return A stream writer instance
+ */
+ public static NormalizedNodeStreamWriter create(final SchemaContext schemaContext, final Writer writer) {
+ return new JSONNormalizedNodeStreamWriter(schemaContext, writer, 0);
+ }
+
+ /**
+ * Create a new stream writer, which writes to the specified output stream.
+ *
+ * @param schemaContext Schema context
+ * @param writer Output writer
+ * @param indentSize indentation size
+ * @return A stream writer instance
+ */
+ public static NormalizedNodeStreamWriter create(final SchemaContext schemaContext, final Writer writer, final int indentSize) {
+ return new JSONNormalizedNodeStreamWriter(schemaContext, writer, indentSize);
+ }
+
+ @Override
+ public void leafNode(final NodeIdentifier name, final Object value) throws IOException {
+ separateElementFromPreviousElement();
+ writeJsonIdentifier(name);
+ currentNamespace = stack.peek().getNamespace();
+ writeValue(value.toString());
+ separateNextSiblingsWithComma();
+ }
+
+ @Override
+ public void startLeafSet(final NodeIdentifier name, final int childSizeHint) throws IOException {
+ separateElementFromPreviousElement();
+ stack.push(new TypeInfo(NodeType.LIST, name.getNodeType().getNamespace()));
+ writeJsonIdentifier(name);
+ writeStartList();
+ indentRight();
+ }
+
+ @Override
+ public void leafSetEntryNode(final Object value) throws IOException {
+ separateElementFromPreviousElement();
+ writeValue(value.toString());
+ separateNextSiblingsWithComma();
+ }
+
+ @Override
+ public void startContainerNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
+ separateElementFromPreviousElement();
+ stack.push(new TypeInfo(NodeType.OBJECT, name.getNodeType().getNamespace()));
+ writeJsonIdentifier(name);
+ writeStartObject();
+ indentRight();
+ }
+
+ @Override
+ public void startUnkeyedList(final NodeIdentifier name, final int childSizeHint) throws IOException {
+ separateElementFromPreviousElement();
+ stack.push(new TypeInfo(NodeType.LIST, name.getNodeType().getNamespace()));
+ writeJsonIdentifier(name);
+ writeStartList();
+ indentRight();
+ }
+
+ @Override
+ public void startUnkeyedListItem(final NodeIdentifier name, final int childSizeHint) throws IOException {
+ stack.push(new TypeInfo(NodeType.OBJECT, name.getNodeType().getNamespace()));
+ separateElementFromPreviousElement();
+ writeStartObject();
+ indentRight();
+ }
+
+ @Override
+ public void startMapNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
+ separateElementFromPreviousElement();
+ stack.push(new TypeInfo(NodeType.LIST, name.getNodeType().getNamespace()));
+ writeJsonIdentifier(name);
+ writeStartList();
+ indentRight();
+ }
+
+ @Override
+ public void startMapEntryNode(final NodeIdentifierWithPredicates identifier, final int childSizeHint)
+ throws IOException {
+ stack.push(new TypeInfo(NodeType.OBJECT, identifier.getNodeType().getNamespace()));
+ separateElementFromPreviousElement();
+ writeStartObject();
+ indentRight();
+ }
+
+ @Override
+ public void startOrderedMapNode(final NodeIdentifier name, final int childSizeHint) throws IOException {
+ stack.push(new TypeInfo(NodeType.LIST, name.getNodeType().getNamespace()));
+ separateElementFromPreviousElement();
+ writeJsonIdentifier(name);
+ writeStartList();
+ indentRight();
+ }
+
+ @Override
+ public void startChoiceNode(final NodeIdentifier name, final int childSizeHint) throws IllegalArgumentException {
+ handleInvisibleNode(name.getNodeType().getNamespace());
+ }
+
+ @Override
+ public void startAugmentationNode(final AugmentationIdentifier identifier) throws IllegalArgumentException {
+ handleInvisibleNode(currentNamespace);
+ }
+
+ @Override
+ public void anyxmlNode(final NodeIdentifier name, final Object value) throws IOException {
+ separateElementFromPreviousElement();
+ writeJsonIdentifier(name);
+ currentNamespace = stack.peek().getNamespace();
+ writeValue(value.toString());
+ separateNextSiblingsWithComma();
+ }
+
+ @Override
+ public void endNode() throws IOException {
+ switch (stack.peek().getType()) {
+ case LIST:
+ indentLeft();
+ newLine();
+ writer.append(']');
+ break;
+ case OBJECT:
+ indentLeft();
+ newLine();
+ writer.append('}');
+ break;
+ default:
+ break;
+ }
+ stack.pop();
+ currentNamespace = stack.isEmpty() ? null : stack.peek().getNamespace();
+ separateNextSiblingsWithComma();
+ }
+
+ private void separateElementFromPreviousElement() throws IOException {
+ if (!stack.isEmpty() && stack.peek().hasAtLeastOneChild()) {
+ writer.append(',');
+ }
+ newLine();
+ }
+
+ private void newLine() throws IOException {
+ if (indent != null) {
+ writer.append('\n');
+
+ for (int i = 0; i < currentDepth; i++) {
+ writer.append(indent);
+ }
+ }
+ }
+
+ private void separateNextSiblingsWithComma() {
+ if (!stack.isEmpty()) {
+ stack.peek().setHasAtLeastOneChild(true);
+ }
+ }
+
+ /**
+ * Invisible nodes have to be also pushed to stack because of pairing of start*() and endNode() methods. Information
+ * about child existing (due to printing comma) has to be transfered to invisible node.
+ */
+ private void handleInvisibleNode(final URI uri) {
+ TypeInfo typeInfo = new TypeInfo(NodeType.OTHER, uri);
+ typeInfo.setHasAtLeastOneChild(stack.peek().hasAtLeastOneChild());
+ stack.push(typeInfo);
+ }
+
+ private void writeStartObject() throws IOException {
+ writer.append('{');
+ }
+
+ private void writeStartList() throws IOException {
+ writer.append('[');
+ }
+
+ private void writeModulName(final URI namespace) throws IOException {
+ if (this.currentNamespace == null || namespace != this.currentNamespace) {
+ Module module = schemaContext.findModuleByNamespaceAndRevision(namespace, null);
+ writer.append(module.getName());
+ writer.append(':');
+ currentNamespace = namespace;
+ }
+ }
+
+ private void writeValue(final String value) throws IOException {
+ writer.append('"');
+ writer.append(value);
+ writer.append('"');
+ }
+
+ private void writeJsonIdentifier(final NodeIdentifier name) throws IOException {
+ writer.append('"');
+ writeModulName(name.getNodeType().getNamespace());
+ writer.append(name.getNodeType().getLocalName());
+ writer.append("\":");
+ }
+
+ private void indentRight() {
+ currentDepth++;
+ }
+
+ private void indentLeft() {
+ currentDepth--;
+ }
+
+ @Override
+ public void flush() throws IOException {
+ writer.flush();
+ }
+
+ @Override
+ public void close() throws IOException {
+ writer.flush();
+ writer.close();
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 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.codec.gson;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Optional;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Splitter;
+import com.google.common.collect.Iterators;
+import com.google.gson.JsonIOException;
+import com.google.gson.JsonParseException;
+import com.google.gson.JsonSyntaxException;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonToken;
+import com.google.gson.stream.MalformedJsonException;
+
+import java.io.Closeable;
+import java.io.EOFException;
+import java.io.Flushable;
+import java.io.IOException;
+import java.net.URI;
+import java.security.InvalidParameterException;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Deque;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import org.opendaylight.yangtools.concepts.Codec;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.codec.gson.helpers.IdentityValuesDTO;
+import org.opendaylight.yangtools.yang.data.codec.gson.helpers.RestCodecFactory;
+import org.opendaylight.yangtools.yang.data.codec.gson.helpers.RestUtil;
+import org.opendaylight.yangtools.yang.data.codec.gson.helpers.RestUtil.PrefixMapingFromJson;
+import org.opendaylight.yangtools.yang.data.codec.gson.helpers.SchemaContextUtils;
+import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+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.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
+
+/**
+ * This class parses JSON elements from a GSON JsonReader. It disallows multiple elements of the same name unlike the
+ * default GSON JsonParser.
+ */
+@Beta
+public final class JsonParserStream implements Closeable, Flushable {
+ private static final Splitter COLON_SPLITTER = Splitter.on(':');
+
+ private final Deque<URI> namespaces = new ArrayDeque<>();
+ private final NormalizedNodeStreamWriter writer;
+ private final SchemaContextUtils utils;
+ private final RestCodecFactory codecs;
+ private final SchemaContext schema;
+
+ private JsonParserStream(final NormalizedNodeStreamWriter writer, final SchemaContext schemaContext) {
+ this.schema = Preconditions.checkNotNull(schemaContext);
+ this.utils = SchemaContextUtils.create(schemaContext);
+ this.writer = Preconditions.checkNotNull(writer);
+ this.codecs = RestCodecFactory.create(utils);
+ }
+
+ public static JsonParserStream create(final NormalizedNodeStreamWriter writer, final SchemaContext schemaContext) {
+ return new JsonParserStream(writer, schemaContext);
+ }
+
+ public JsonParserStream parse(final JsonReader reader) throws JsonIOException, JsonSyntaxException {
+ // code copied from gson's JsonParser and Stream classes
+
+ boolean lenient = reader.isLenient();
+ reader.setLenient(true);
+ boolean isEmpty = true;
+ try {
+ reader.peek();
+ isEmpty = false;
+ CompositeNodeDataWithSchema compositeNodeDataWithSchema = new CompositeNodeDataWithSchema(schema);
+ read(reader, compositeNodeDataWithSchema);
+ compositeNodeDataWithSchema.writeToStream(writer);
+
+ return this;
+ // return read(reader);
+ } catch (EOFException e) {
+ if (isEmpty) {
+ return this;
+ // return JsonNull.INSTANCE;
+ }
+ // The stream ended prematurely so it is likely a syntax error.
+ throw new JsonSyntaxException(e);
+ } catch (MalformedJsonException e) {
+ throw new JsonSyntaxException(e);
+ } catch (IOException e) {
+ throw new JsonIOException(e);
+ } catch (NumberFormatException e) {
+ throw new JsonSyntaxException(e);
+ } catch (StackOverflowError | OutOfMemoryError e) {
+ throw new JsonParseException("Failed parsing JSON source: " + reader + " to Json", e);
+ } finally {
+ reader.setLenient(lenient);
+ }
+ }
+
+ public void read(final JsonReader in, final AbstractNodeDataWithSchema parent) throws IOException {
+
+ final JsonToken peek = in.peek();
+ Optional<String> value = Optional.absent();
+ switch (peek) {
+ case STRING:
+ case NUMBER:
+ value = Optional.of(in.nextString());
+ break;
+ case BOOLEAN:
+ value = Optional.of(Boolean.toString(in.nextBoolean()));
+ break;
+ case NULL:
+ in.nextNull();
+ value = Optional.of((String) null);
+ break;
+ default:
+ break;
+ }
+ if (value.isPresent()) {
+ final Object translatedValue = translateValueByType(value.get(), parent.getSchema());
+ ((SimpleNodeDataWithSchema) parent).setValue(translatedValue);
+ }
+
+ switch (peek) {
+ case BEGIN_ARRAY:
+ in.beginArray();
+ while (in.hasNext()) {
+ AbstractNodeDataWithSchema newChild = null;
+ if (parent instanceof ListNodeDataWithSchema) {
+ newChild = new ListEntryNodeDataWithSchema(parent.getSchema());
+ ((CompositeNodeDataWithSchema) parent).addChild(newChild);
+ } else if (parent instanceof LeafListNodeDataWithSchema) {
+ newChild = new LeafListEntryNodeDataWithSchema(parent.getSchema());
+ ((CompositeNodeDataWithSchema) parent).addChild(newChild);
+ }
+ read(in, newChild);
+ }
+ in.endArray();
+ return;
+ case BEGIN_OBJECT:
+ Set<String> namesakes = new HashSet<>();
+ in.beginObject();
+ while (in.hasNext()) {
+ final String jsonElementName = in.nextName();
+ final NamespaceAndName namespaceAndName = resolveNamespace(jsonElementName);
+ final String localName = namespaceAndName.getName();
+ addNamespace(namespaceAndName.getUri());
+ if (namesakes.contains(jsonElementName)) {
+ throw new JsonSyntaxException("Duplicate name " + jsonElementName + " in JSON input.");
+ }
+ namesakes.add(jsonElementName);
+ final Deque<DataSchemaNode> childDataSchemaNodes = findSchemaNodeByNameAndNamespace(parent.getSchema(),
+ localName, getCurrentNamespace());
+ if (childDataSchemaNodes.isEmpty()) {
+ throw new IllegalStateException("Schema for node with name " + localName + " and namespace "
+ + getCurrentNamespace() + " doesn't exist.");
+ }
+
+ AbstractNodeDataWithSchema newChild;
+ newChild = ((CompositeNodeDataWithSchema) parent).addChild(childDataSchemaNodes);
+// FIXME:anyxml data shouldn't be skipped but should be loaded somehow. will be specified after 17AUG2014
+ if (newChild instanceof AnyXmlNodeDataWithSchema) {
+ in.skipValue();
+ } else {
+ read(in, newChild);
+ }
+ removeNamespace();
+ }
+ in.endObject();
+ return;
+ case END_DOCUMENT:
+ case NAME:
+ case END_OBJECT:
+ case END_ARRAY:
+ }
+ }
+
+ private Object translateValueByType(final String value, final DataSchemaNode node) {
+ final TypeDefinition<? extends Object> typeDefinition = typeDefinition(node);
+ if (typeDefinition == null) {
+ return value;
+ }
+
+ final Object inputValue;
+ if (typeDefinition instanceof IdentityrefTypeDefinition) {
+ inputValue = valueAsIdentityRef(value);
+ } else if (typeDefinition instanceof InstanceIdentifierTypeDefinition) {
+ inputValue = valueAsInstanceIdentifier(value);
+ } else {
+ inputValue = value;
+ }
+
+ // FIXME: extract this as a cacheable context?
+ final Codec<Object, Object> codec = codecs.codecFor(typeDefinition);
+ if (codec == null) {
+ return null;
+ }
+ return codec.deserialize(inputValue);
+ }
+
+ private static TypeDefinition<? extends Object> typeDefinition(final DataSchemaNode node) {
+ TypeDefinition<?> baseType = null;
+ if (node instanceof LeafListSchemaNode) {
+ baseType = ((LeafListSchemaNode) node).getType();
+ } else if (node instanceof LeafSchemaNode) {
+ baseType = ((LeafSchemaNode) node).getType();
+ } else if (node instanceof AnyXmlSchemaNode) {
+ return null;
+ } else {
+ throw new IllegalArgumentException("Unhandled parameter types: " + Arrays.<Object> asList(node).toString());
+ }
+
+ if (baseType != null) {
+ while (baseType.getBaseType() != null) {
+ baseType = baseType.getBaseType();
+ }
+ }
+ return baseType;
+ }
+
+ private static Object valueAsInstanceIdentifier(final String value) {
+ // it could be instance-identifier Built-In Type
+ if (!value.isEmpty() && value.charAt(0) == '/') {
+ IdentityValuesDTO resolvedValue = RestUtil.asInstanceIdentifier(value, new PrefixMapingFromJson());
+ if (resolvedValue != null) {
+ return resolvedValue;
+ }
+ }
+ throw new InvalidParameterException("Value for instance-identifier doesn't have correct format");
+ }
+
+ private static IdentityValuesDTO valueAsIdentityRef(final String value) {
+ // it could be identityref Built-In Type
+ URI namespace = getNamespaceFor(value);
+ if (namespace != null) {
+ return new IdentityValuesDTO(namespace.toString(), getLocalNameFor(value), null, value);
+ }
+ throw new InvalidParameterException("Value for identityref has to be in format moduleName:localName.");
+ }
+
+ private static URI getNamespaceFor(final String jsonElementName) {
+ final Iterator<String> it = COLON_SPLITTER.split(jsonElementName).iterator();
+
+ // The string needs to me in form "moduleName:localName"
+ if (it.hasNext()) {
+ final String maybeURI = it.next();
+ if (Iterators.size(it) == 1) {
+ return URI.create(maybeURI);
+ }
+ }
+
+ return null;
+ }
+
+ private static String getLocalNameFor(final String jsonElementName) {
+ final Iterator<String> it = COLON_SPLITTER.split(jsonElementName).iterator();
+
+ // The string needs to me in form "moduleName:localName"
+ final String ret = Iterators.get(it, 1, null);
+ return ret != null && !it.hasNext() ? ret : jsonElementName;
+ }
+
+ private void removeNamespace() {
+ namespaces.pop();
+ }
+
+ private void addNamespace(final Optional<URI> namespace) {
+ if (!namespace.isPresent()) {
+ if (namespaces.isEmpty()) {
+ throw new IllegalStateException("Namespace has to be specified at top level.");
+ } else {
+ namespaces.push(namespaces.peek());
+ }
+ } else {
+ namespaces.push(namespace.get());
+ }
+ }
+
+ private NamespaceAndName resolveNamespace(final String childName) {
+ int lastIndexOfColon = childName.lastIndexOf(":");
+ String moduleNamePart = null;
+ String nodeNamePart = null;
+ URI namespace = null;
+ if (lastIndexOfColon != -1) {
+ moduleNamePart = childName.substring(0, lastIndexOfColon);
+ nodeNamePart = childName.substring(lastIndexOfColon + 1);
+ namespace = utils.findNamespaceByModuleName(moduleNamePart);
+ } else {
+ nodeNamePart = childName;
+ }
+
+ Optional<URI> namespaceOpt = namespace == null ? Optional.<URI> absent() : Optional.of(namespace);
+ return new NamespaceAndName(nodeNamePart, namespaceOpt);
+ }
+
+ private URI getCurrentNamespace() {
+ return namespaces.peek();
+ }
+
+ /**
+ * Returns stack of schema nodes via which it was necessary to prass to get schema node with specified
+ * {@code childName} and {@code namespace}
+ *
+ * @param dataSchemaNode
+ * @param childName
+ * @param namespace
+ * @return stack of schema nodes via which it was passed through. If found schema node is dirrect child then stack
+ * contains only one node. If it is found under choice and case then stack should conains 2*n+1 element
+ * (where n is number of choices through it was passed)
+ */
+ private Deque<DataSchemaNode> findSchemaNodeByNameAndNamespace(final DataSchemaNode dataSchemaNode,
+ final String childName, final URI namespace) {
+ final Deque<DataSchemaNode> result = new ArrayDeque<>();
+ List<ChoiceNode> childChoices = new ArrayList<>();
+ if (dataSchemaNode instanceof DataNodeContainer) {
+ for (DataSchemaNode childNode : ((DataNodeContainer) dataSchemaNode).getChildNodes()) {
+ if (childNode instanceof ChoiceNode) {
+ childChoices.add((ChoiceNode) childNode);
+ } else {
+ final QName childQName = childNode.getQName();
+ if (childQName.getLocalName().equals(childName) && childQName.getNamespace().equals(namespace)) {
+ result.push(childNode);
+ return result;
+ }
+ }
+ }
+ }
+ // try to find data schema node in choice (looking for first match)
+ for (ChoiceNode choiceNode : childChoices) {
+ for (ChoiceCaseNode concreteCase : choiceNode.getCases()) {
+ Deque<DataSchemaNode> resultFromRecursion = findSchemaNodeByNameAndNamespace(concreteCase, childName,
+ namespace);
+ if (!resultFromRecursion.isEmpty()) {
+ resultFromRecursion.push(concreteCase);
+ resultFromRecursion.push(choiceNode);
+ return resultFromRecursion;
+ }
+ }
+ }
+ return result;
+ }
+
+ private static class NamespaceAndName {
+ private final Optional<URI> uri;
+ private final String name;
+
+ public NamespaceAndName(final String name, final Optional<URI> uri) {
+ this.name = name;
+ this.uri = uri;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public Optional<URI> getUri() {
+ return uri;
+ }
+ }
+
+ @Override
+ public void flush() throws IOException {
+ writer.flush();
+ }
+
+ @Override
+ public void close() throws IOException {
+ writer.flush();
+ writer.close();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 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.codec.gson;
+
+import java.io.IOException;
+
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+class LeafListEntryNodeDataWithSchema extends SimpleNodeDataWithSchema {
+ public LeafListEntryNodeDataWithSchema(final DataSchemaNode dataSchemaNode) {
+ super(dataSchemaNode);
+ }
+
+ @Override
+ protected void writeToStream(final NormalizedNodeStreamWriter nnStreamWriter) throws IOException {
+ nnStreamWriter.leafSetEntryNode(getValue());
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 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.codec.gson;
+
+import static org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter.UNKNOWN_SIZE;
+
+import java.io.IOException;
+
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+class LeafListNodeDataWithSchema extends CompositeNodeDataWithSchema {
+ public LeafListNodeDataWithSchema(final DataSchemaNode schema) {
+ super(schema);
+ }
+
+ @Override
+ protected void writeToStream(final NormalizedNodeStreamWriter nnStreamWriter) throws IOException {
+ nnStreamWriter.startLeafSet(provideNodeIdentifier(), UNKNOWN_SIZE);
+ super.writeToStream(nnStreamWriter);
+ nnStreamWriter.endNode();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 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.codec.gson;
+
+import java.io.IOException;
+
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+class LeafNodeDataWithSchema extends SimpleNodeDataWithSchema {
+
+ public LeafNodeDataWithSchema(final DataSchemaNode schema) {
+ super(schema);
+ }
+
+ @Override
+ protected void writeToStream(final NormalizedNodeStreamWriter nnStreamWriter) throws IOException {
+ nnStreamWriter.leafNode(provideNodeIdentifier(), getValue());
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 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.codec.gson;
+
+import static org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter.UNKNOWN_SIZE;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifierWithPredicates;
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+
+class ListEntryNodeDataWithSchema extends CompositeNodeDataWithSchema {
+
+ private final Map<QName, SimpleNodeDataWithSchema> qNameToKeys = new HashMap<>();
+
+ public ListEntryNodeDataWithSchema(final DataSchemaNode schema) {
+ super(schema);
+ }
+
+ @Override
+ public void addChild(final AbstractNodeDataWithSchema newChild) {
+ DataSchemaNode childSchema = newChild.getSchema();
+ if (childSchema instanceof LeafSchemaNode && isPartOfKey((LeafSchemaNode) childSchema)) {
+ qNameToKeys.put(childSchema.getQName(), (SimpleNodeDataWithSchema)newChild);
+ }
+ super.addChild(newChild);
+ }
+
+ private boolean isPartOfKey(final LeafSchemaNode potentialKey) {
+ List<QName> keys = ((ListSchemaNode) getSchema()).getKeyDefinition();
+ for (QName qName : keys) {
+ if (qName.equals(potentialKey.getQName())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ protected void writeToStream(final NormalizedNodeStreamWriter nnStreamWriter) throws IOException {
+ int keyCount = ((ListSchemaNode) getSchema()).getKeyDefinition().size();
+ if (keyCount == 0) {
+ nnStreamWriter.startUnkeyedListItem(provideNodeIdentifier(), UNKNOWN_SIZE);
+ super.writeToStream(nnStreamWriter);
+ nnStreamWriter.endNode();
+ } else if (keyCount == qNameToKeys.size()) {
+ nnStreamWriter.startMapEntryNode(provideNodeIdentifierWithPredicates(), UNKNOWN_SIZE);
+ super.writeToStream(nnStreamWriter);
+ nnStreamWriter.endNode();
+ } else {
+ throw new IllegalStateException("Some of keys of " + getSchema().getQName() + " are missing in input.");
+ }
+ }
+
+ private NodeIdentifierWithPredicates provideNodeIdentifierWithPredicates() {
+ Map<QName, Object> qNameToPredicateValues = new HashMap<>();
+
+ for (SimpleNodeDataWithSchema simpleNodeDataWithSchema : qNameToKeys.values()) {
+ qNameToPredicateValues.put(simpleNodeDataWithSchema.getSchema().getQName(), simpleNodeDataWithSchema.getValue());
+ }
+
+ return new NodeIdentifierWithPredicates(getSchema().getQName(), qNameToPredicateValues);
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 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.codec.gson;
+
+import static org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter.UNKNOWN_SIZE;
+
+import java.io.IOException;
+
+import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+
+class ListNodeDataWithSchema extends CompositeNodeDataWithSchema {
+
+ public ListNodeDataWithSchema(final DataSchemaNode schema) {
+ super(schema);
+ }
+
+ @Override
+ protected void writeToStream(final NormalizedNodeStreamWriter nnStreamWriter) throws IOException {
+ if (!((ListSchemaNode) getSchema()).getKeyDefinition().isEmpty()) {
+ nnStreamWriter.startMapNode(provideNodeIdentifier(), UNKNOWN_SIZE);
+ } else {
+ nnStreamWriter.startUnkeyedList(provideNodeIdentifier(), UNKNOWN_SIZE);
+ }
+ super.writeToStream(nnStreamWriter);
+ nnStreamWriter.endNode();
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 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.codec.gson;
+
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+
+abstract class SimpleNodeDataWithSchema extends AbstractNodeDataWithSchema {
+
+ private Object value;
+
+ public SimpleNodeDataWithSchema(final DataSchemaNode dataSchemaNode) {
+ super(dataSchemaNode);
+ }
+
+ void setValue(final Object value) {
+ this.value = value;
+ }
+
+ public Object getValue() {
+ return value;
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 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.codec.gson.helpers;
+
+import com.google.common.base.Preconditions;
+
+import java.net.URI;
+
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+abstract class AbstractCodecImpl {
+ private static final Logger LOG = LoggerFactory.getLogger(AbstractCodecImpl.class);
+ private final SchemaContextUtils schema;
+
+ protected AbstractCodecImpl(final SchemaContextUtils schema) {
+ this.schema = Preconditions.checkNotNull(schema);
+ }
+
+ protected final SchemaContextUtils getSchema() {
+ return schema;
+ }
+
+ protected final Module getModuleByNamespace(final String namespace) {
+ URI validNamespace = resolveValidNamespace(namespace);
+
+ Module module = schema.findModuleByNamespace(validNamespace);
+ if (module == null) {
+ LOG.info("Module for namespace " + validNamespace + " wasn't found.");
+ return null;
+ }
+ return module;
+ }
+
+ protected final URI resolveValidNamespace(final String namespace) {
+ URI validNamespace = schema.findNamespaceByModuleName(namespace);
+ if (validNamespace == null) {
+ validNamespace = URI.create(namespace);
+ }
+
+ return validNamespace;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 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.codec.gson.helpers;
+
+import com.google.common.annotations.Beta;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * This class is implementation-internal and subject to change. Please do not use it.
+ */
+@Beta
+public final class IdentityValuesDTO {
+
+ private final List<IdentityValue> elementData = new ArrayList<>();
+ private final String originValue;
+
+ public IdentityValuesDTO(final String namespace, final String value, final String prefix, final String originValue) {
+ elementData.add(new IdentityValue(namespace, value, prefix));
+ this.originValue = originValue;
+ }
+
+ public IdentityValuesDTO(final String originValue) {
+ this.originValue = originValue;
+ }
+
+ public IdentityValuesDTO() {
+ originValue = null;
+ }
+
+ public void add(final String namespace, final String value, final String prefix) {
+ elementData.add(new IdentityValue(namespace, value, prefix));
+ }
+
+ public void add(final IdentityValue identityValue) {
+ elementData.add(identityValue);
+ }
+
+ public List<IdentityValue> getValuesWithNamespaces() {
+ return Collections.unmodifiableList(elementData);
+ }
+
+ @Override
+ public String toString() {
+ return elementData.toString();
+ }
+
+ public String getOriginValue() {
+ return originValue;
+ }
+
+ public static final class IdentityValue {
+
+ private final String namespace;
+ private final String value;
+ private final String prefix;
+ private List<Predicate> predicates;
+
+ public IdentityValue(final String namespace, final String value, final String prefix) {
+ this.namespace = namespace;
+ this.value = value;
+ this.prefix = prefix;
+ }
+
+ public String getNamespace() {
+ return namespace;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ public String getPrefix() {
+ return prefix;
+ }
+
+ public List<Predicate> getPredicates() {
+ if (predicates == null) {
+ return Collections.emptyList();
+ }
+ return Collections.unmodifiableList(predicates);
+ }
+
+ public void setPredicates(final List<Predicate> predicates) {
+ this.predicates = predicates;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ if (namespace != null) {
+ sb.append(namespace);
+ }
+ if (prefix != null) {
+ sb.append("(").append(prefix).append(")");
+ }
+ if (value != null) {
+ sb.append(" - ").append(value);
+ }
+ if (predicates != null && !predicates.isEmpty()) {
+ for (Predicate predicate : predicates) {
+ sb.append("[");
+ predicate.toString();
+ sb.append("]");
+ }
+ }
+ return sb.toString();
+ }
+
+ }
+
+ public static final class Predicate {
+
+ private final IdentityValue name;
+ private final String value;
+
+ public Predicate(final IdentityValue name, final String value) {
+ super();
+ this.name = name;
+ this.value = value;
+ }
+
+ public IdentityValue getName() {
+ return name;
+ }
+
+ public String getValue() {
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ if (name != null) {
+ sb.append(name.toString());
+ }
+ if (value != null) {
+ sb.append("=").append(value);
+ }
+ return sb.toString();
+ }
+
+ public boolean isLeafList() {
+ return name == null ? true : false;
+ }
+
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 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.codec.gson.helpers;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.codec.IdentityrefCodec;
+import org.opendaylight.yangtools.yang.data.codec.gson.helpers.IdentityValuesDTO.IdentityValue;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+class IdentityrefCodecImpl extends AbstractCodecImpl implements IdentityrefCodec<IdentityValuesDTO> {
+ private static final Logger LOG = LoggerFactory.getLogger(IdentityrefCodecImpl.class);
+
+ IdentityrefCodecImpl(final SchemaContextUtils schema) {
+ super(schema);
+ }
+
+ @Override
+ public IdentityValuesDTO serialize(final QName data) {
+ return new IdentityValuesDTO(data.getNamespace().toString(), data.getLocalName(), data.getPrefix(), null);
+ }
+
+ @Override
+ public QName deserialize(final IdentityValuesDTO data) {
+ IdentityValue valueWithNamespace = data.getValuesWithNamespaces().get(0);
+ Module module = getModuleByNamespace(valueWithNamespace.getNamespace());
+ if (module == null) {
+ LOG.info("Module was not found for namespace {}", valueWithNamespace.getNamespace());
+ LOG.info("Idenetityref will be translated as NULL for data - {}", String.valueOf(valueWithNamespace));
+ return null;
+ }
+
+ return QName.create(module.getNamespace(), module.getRevision(), valueWithNamespace.getValue());
+ }
+
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2014 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.codec.gson.helpers;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+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.codec.InstanceIdentifierCodec;
+import org.opendaylight.yangtools.yang.data.codec.gson.helpers.IdentityValuesDTO.IdentityValue;
+import org.opendaylight.yangtools.yang.data.codec.gson.helpers.IdentityValuesDTO.Predicate;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+class InstanceIdentifierCodecImpl extends AbstractCodecImpl implements InstanceIdentifierCodec<IdentityValuesDTO> {
+ private static final Logger LOG = LoggerFactory.getLogger(InstanceIdentifierCodecImpl.class);
+
+ InstanceIdentifierCodecImpl(final SchemaContextUtils schema) {
+ super(schema);
+ }
+
+ @Override
+ public IdentityValuesDTO serialize(final YangInstanceIdentifier data) {
+ IdentityValuesDTO identityValuesDTO = new IdentityValuesDTO();
+ for (PathArgument pathArgument : data.getPathArguments()) {
+ IdentityValue identityValue = qNameToIdentityValue(pathArgument.getNodeType());
+ if (pathArgument instanceof NodeIdentifierWithPredicates && identityValue != null) {
+ List<Predicate> predicates = keyValuesToPredicateList(((NodeIdentifierWithPredicates) pathArgument)
+ .getKeyValues());
+ identityValue.setPredicates(predicates);
+ } else if (pathArgument instanceof NodeWithValue && identityValue != null) {
+ List<Predicate> predicates = new ArrayList<>();
+ String value = String.valueOf(((NodeWithValue) pathArgument).getValue());
+ predicates.add(new Predicate(null, value));
+ identityValue.setPredicates(predicates);
+ }
+ identityValuesDTO.add(identityValue);
+ }
+ return identityValuesDTO;
+ }
+
+ @Override
+ public YangInstanceIdentifier deserialize(final IdentityValuesDTO data) {
+ List<PathArgument> result = new ArrayList<PathArgument>();
+ IdentityValue valueWithNamespace = data.getValuesWithNamespaces().get(0);
+ Module module = getModuleByNamespace(valueWithNamespace.getNamespace());
+ if (module == null) {
+ LOG.info("Module by namespace '{}' of first node in instance-identiefier was not found.",
+ valueWithNamespace.getNamespace());
+ LOG.info("Instance-identifier will be translated as NULL for data - {}",
+ String.valueOf(valueWithNamespace.getValue()));
+ return null;
+ }
+
+ DataNodeContainer parentContainer = module;
+ List<IdentityValue> identities = data.getValuesWithNamespaces();
+ for (int i = 0; i < identities.size(); i++) {
+ IdentityValue identityValue = identities.get(i);
+ URI validNamespace = resolveValidNamespace(identityValue.getNamespace());
+ DataSchemaNode node = getSchema().findInstanceDataChildByNameAndNamespace(
+ parentContainer, identityValue.getValue(), validNamespace);
+ if (node == null) {
+ LOG.info("'{}' node was not found in {}", identityValue, parentContainer.getChildNodes());
+ LOG.info("Instance-identifier will be translated as NULL for data - {}",
+ String.valueOf(identityValue.getValue()));
+ return null;
+ }
+ QName qName = node.getQName();
+ PathArgument pathArgument = null;
+ if (identityValue.getPredicates().isEmpty()) {
+ pathArgument = new NodeIdentifier(qName);
+ } else {
+ if (node instanceof LeafListSchemaNode) { // predicate is value of leaf-list entry
+ Predicate leafListPredicate = identityValue.getPredicates().get(0);
+ if (!leafListPredicate.isLeafList()) {
+ LOG.info("Predicate's data is not type of leaf-list. It should be in format \".='value'\"");
+ LOG.info("Instance-identifier will be translated as NULL for data - {}",
+ String.valueOf(identityValue.getValue()));
+ return null;
+ }
+ pathArgument = new NodeWithValue(qName, leafListPredicate.getValue());
+ } else if (node instanceof ListSchemaNode) { // predicates are keys of list
+ DataNodeContainer listNode = (DataNodeContainer) node;
+ Map<QName, Object> predicatesMap = new HashMap<>();
+ for (Predicate predicate : identityValue.getPredicates()) {
+ validNamespace = resolveValidNamespace(predicate.getName().getNamespace());
+ DataSchemaNode listKey = getSchema()
+ .findInstanceDataChildByNameAndNamespace(listNode, predicate.getName().getValue(),
+ validNamespace);
+ predicatesMap.put(listKey.getQName(), predicate.getValue());
+ }
+ pathArgument = new NodeIdentifierWithPredicates(qName, predicatesMap);
+ } else {
+ LOG.info("Node {} is not List or Leaf-list.", node);
+ LOG.info("Instance-identifier will be translated as NULL for data - {}",
+ String.valueOf(identityValue.getValue()));
+ return null;
+ }
+ }
+ result.add(pathArgument);
+ if (i < identities.size() - 1) { // last element in instance-identifier can be other than
+ // DataNodeContainer
+ if (node instanceof DataNodeContainer) {
+ parentContainer = (DataNodeContainer) node;
+ } else {
+ LOG.info("Node {} isn't instance of DataNodeContainer", node);
+ LOG.info("Instance-identifier will be translated as NULL for data - {}",
+ String.valueOf(identityValue.getValue()));
+ return null;
+ }
+ }
+ }
+
+ return result.isEmpty() ? null : YangInstanceIdentifier.create(result);
+ }
+
+ private static List<Predicate> keyValuesToPredicateList(final Map<QName, Object> keyValues) {
+ List<Predicate> result = new ArrayList<>();
+ for (QName qName : keyValues.keySet()) {
+ Object value = keyValues.get(qName);
+ result.add(new Predicate(qNameToIdentityValue(qName), String.valueOf(value)));
+ }
+ return result;
+ }
+
+ private static IdentityValue qNameToIdentityValue(final QName qName) {
+ if (qName != null) {
+ // FIXME: the prefix here is completely arbitrary
+ return new IdentityValue(qName.getNamespace().toString(), qName.getLocalName(), qName.getPrefix());
+ }
+ return null;
+ }
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2014 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.codec.gson.helpers;
+
+import org.opendaylight.yangtools.yang.data.api.codec.LeafrefCodec;
+
+class LeafrefCodecImpl implements LeafrefCodec<String> {
+
+ @Override
+ public String serialize(final Object data) {
+ return String.valueOf(data);
+ }
+
+ @Override
+ public Object deserialize(final String data) {
+ return data;
+ }
+
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2014 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.codec.gson.helpers;
+
+import org.opendaylight.yangtools.concepts.Codec;
+import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.IdentityrefTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.type.LeafrefTypeDefinition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@SuppressWarnings("rawtypes")
+final class ObjectCodec extends AbstractCodecImpl implements Codec<Object, Object> {
+ public static final Codec LEAFREF_DEFAULT_CODEC = new LeafrefCodecImpl();
+ private static final Logger LOG = LoggerFactory.getLogger(RestCodecFactory.class);
+ private final Codec instanceIdentifier;
+ private final Codec identityrefCodec;
+ private final TypeDefinition<?> type;
+
+ ObjectCodec(final SchemaContextUtils schema, final TypeDefinition<?> typeDefinition) {
+ super(schema);
+ type = RestUtil.resolveBaseTypeFrom(typeDefinition);
+ if (type instanceof IdentityrefTypeDefinition) {
+ identityrefCodec = new IdentityrefCodecImpl(schema);
+ } else {
+ identityrefCodec = null;
+ }
+ if (type instanceof InstanceIdentifierTypeDefinition) {
+ instanceIdentifier = new InstanceIdentifierCodecImpl(schema);
+ } else {
+ instanceIdentifier = null;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Object deserialize(final Object input) {
+ try {
+ if (type instanceof IdentityrefTypeDefinition) {
+ if (input instanceof IdentityValuesDTO) {
+ return identityrefCodec.deserialize(input);
+ }
+ LOG.debug("Value is not instance of IdentityrefTypeDefinition but is {}. Therefore NULL is used as translation of - {}",
+ input == null ? "null" : input.getClass(), String.valueOf(input));
+ return null;
+ } else if (type instanceof LeafrefTypeDefinition) {
+ if (input instanceof IdentityValuesDTO) {
+ return LEAFREF_DEFAULT_CODEC.deserialize(((IdentityValuesDTO) input).getOriginValue());
+ }
+ return LEAFREF_DEFAULT_CODEC.deserialize(input);
+ } else if (type instanceof InstanceIdentifierTypeDefinition) {
+ if (input instanceof IdentityValuesDTO) {
+ return instanceIdentifier.deserialize(input);
+ }
+ LOG.info(
+ "Value is not instance of InstanceIdentifierTypeDefinition but is {}. Therefore NULL is used as translation of - {}",
+ input == null ? "null" : input.getClass(), String.valueOf(input));
+ return null;
+ } else {
+ TypeDefinitionAwareCodec<Object, ? extends TypeDefinition<?>> typeAwarecodec = TypeDefinitionAwareCodec
+ .from(type);
+ if (typeAwarecodec != null) {
+ if (input instanceof IdentityValuesDTO) {
+ return typeAwarecodec.deserialize(((IdentityValuesDTO) input).getOriginValue());
+ }
+ return typeAwarecodec.deserialize(String.valueOf(input));
+ } else {
+ LOG.debug("Codec for type \"" + type.getQName().getLocalName()
+ + "\" is not implemented yet.");
+ return null;
+ }
+ }
+ } catch (ClassCastException e) {
+ // TODO remove this catch when everyone use codecs
+ LOG.error("ClassCastException was thrown when codec is invoked with parameter " + String.valueOf(input),
+ e);
+ return null;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public Object serialize(final Object input) {
+ try {
+ if (type instanceof IdentityrefTypeDefinition) {
+ return identityrefCodec.serialize(input);
+ } else if (type instanceof LeafrefTypeDefinition) {
+ return LEAFREF_DEFAULT_CODEC.serialize(input);
+ } else if (type instanceof InstanceIdentifierTypeDefinition) {
+ return instanceIdentifier.serialize(input);
+ } else {
+ TypeDefinitionAwareCodec<Object, ? extends TypeDefinition<?>> typeAwarecodec = TypeDefinitionAwareCodec
+ .from(type);
+ if (typeAwarecodec != null) {
+ return typeAwarecodec.serialize(input);
+ } else {
+ LOG.debug("Codec for type \"" + type.getQName().getLocalName()
+ + "\" is not implemented yet.");
+ return null;
+ }
+ }
+ } catch (ClassCastException e) { // TODO remove this catch when everyone use codecs
+ LOG.error(
+ "ClassCastException was thrown when codec is invoked with parameter " + String.valueOf(input),
+ e);
+ return input;
+ }
+ }
+
+}
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2014 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.codec.gson.helpers;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Preconditions;
+
+import org.opendaylight.yangtools.concepts.Codec;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+
+/**
+ * This class is implementation-internal and subject to change. Please do not use it.
+ */
+@Beta
+public final class RestCodecFactory {
+ private final SchemaContextUtils utils;
+
+ private RestCodecFactory(final SchemaContextUtils utils) {
+ this.utils = Preconditions.checkNotNull(utils);
+ }
+
+ public static RestCodecFactory create(final SchemaContextUtils utils) {
+ return new RestCodecFactory(utils);
+ }
+
+ public final Codec<Object, Object> codecFor(final TypeDefinition<?> typeDefinition) {
+ // FIXME: implement loadingcache
+ return new ObjectCodec(utils, typeDefinition);
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 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.codec.gson.helpers;
+
+
+import com.google.common.annotations.Beta;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import javax.xml.stream.events.StartElement;
+
+import org.opendaylight.yangtools.yang.data.codec.gson.helpers.IdentityValuesDTO.IdentityValue;
+import org.opendaylight.yangtools.yang.data.codec.gson.helpers.IdentityValuesDTO.Predicate;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+
+/**
+ * This class is implementation-internal and subject to change. Please do not use it.
+ */
+@Beta
+public final class RestUtil {
+
+ // FIXME: BUG-1275: this is code duplicates data.impl.codec
+
+ public static final String SQUOTE = "'";
+ public static final String DQUOTE = "\"";
+ private static final Pattern PREDICATE_PATTERN = Pattern.compile("\\[(.*?)\\]");
+
+ public final static TypeDefinition<?> resolveBaseTypeFrom(final TypeDefinition<?> type) {
+ TypeDefinition<?> superType = type;
+ while (superType.getBaseType() != null) {
+ superType = superType.getBaseType();
+ }
+ return superType;
+ }
+
+ public static IdentityValuesDTO asInstanceIdentifier(final String value, final PrefixesMaping prefixMap) {
+ String valueTrimmed = value.trim();
+ if (!valueTrimmed.startsWith("/")) {
+ return null;
+ }
+ String[] xPathParts = valueTrimmed.split("/");
+ if (xPathParts.length < 2) { // must be at least "/pr:node"
+ return null;
+ }
+ IdentityValuesDTO identityValuesDTO = new IdentityValuesDTO(value);
+ for (int i = 1; i < xPathParts.length; i++) {
+ String xPathPartTrimmed = xPathParts[i].trim();
+
+ String xPathPartStr = getIdAndPrefixAsStr(xPathPartTrimmed);
+ IdentityValue identityValue = toIdentity(xPathPartStr, prefixMap);
+ if (identityValue == null) {
+ return null;
+ }
+
+ List<Predicate> predicates = toPredicates(xPathPartTrimmed, prefixMap);
+ if (predicates == null) {
+ return null;
+ }
+ identityValue.setPredicates(predicates);
+
+ identityValuesDTO.add(identityValue);
+ }
+ return identityValuesDTO.getValuesWithNamespaces().isEmpty() ? null : identityValuesDTO;
+ }
+
+ private static String getIdAndPrefixAsStr(final String pathPart) {
+ int predicateStartIndex = pathPart.indexOf("[");
+ return predicateStartIndex == -1 ? pathPart : pathPart.substring(0, predicateStartIndex);
+ }
+
+ private static IdentityValue toIdentity(final String xPathPart, final PrefixesMaping prefixMap) {
+ String xPathPartTrimmed = xPathPart.trim();
+ if (xPathPartTrimmed.isEmpty()) {
+ return null;
+ }
+ String[] prefixAndIdentifier = xPathPartTrimmed.split(":");
+ // it is not "prefix:value"
+ if (prefixAndIdentifier.length != 2) {
+ return null;
+ }
+ String prefix = prefixAndIdentifier[0].trim();
+ String identifier = prefixAndIdentifier[1].trim();
+ if (prefix.isEmpty() || identifier.isEmpty()) {
+ return null;
+ }
+ String namespace = prefixMap.getNamespace(prefix);
+ return new IdentityValue(namespace, identifier, namespace.equals(prefix) ? null : prefix);
+ }
+
+ private static List<Predicate> toPredicates(final String predicatesStr, final PrefixesMaping prefixMap) {
+ List<Predicate> result = new ArrayList<>();
+ List<String> predicates = new ArrayList<>();
+ Matcher matcher = PREDICATE_PATTERN.matcher(predicatesStr);
+ while (matcher.find()) {
+ predicates.add(matcher.group(1).trim());
+ }
+ for (String predicate : predicates) {
+ int indexOfEqualityMark = predicate.indexOf("=");
+ if (indexOfEqualityMark != -1) {
+ String predicateValue = toPredicateValue(predicate.substring(indexOfEqualityMark + 1));
+ if (predicate.startsWith(".")) { // it is leaf-list
+ if (predicateValue == null) {
+ return null;
+ }
+ result.add(new Predicate(null, predicateValue));
+ } else {
+ IdentityValue identityValue = toIdentity(predicate.substring(0, indexOfEqualityMark), prefixMap);
+ if (identityValue == null || predicateValue == null) {
+ return null;
+ }
+ result.add(new Predicate(identityValue, predicateValue));
+ }
+ }
+ }
+ return result;
+ }
+
+ private static String toPredicateValue(final String predicatedValue) {
+ String predicatedValueTrimmed = predicatedValue.trim();
+ if ((predicatedValueTrimmed.startsWith(DQUOTE) || predicatedValueTrimmed.startsWith(SQUOTE))
+ && (predicatedValueTrimmed.endsWith(DQUOTE) || predicatedValueTrimmed.endsWith(SQUOTE))) {
+ return predicatedValueTrimmed.substring(1, predicatedValueTrimmed.length() - 1);
+ }
+ return null;
+ }
+
+ public interface PrefixesMaping {
+ public String getNamespace(String prefix);
+ }
+
+ public static class PrefixMapingFromXml implements PrefixesMaping {
+ StartElement startElement = null;
+
+ public PrefixMapingFromXml(final StartElement startElement) {
+ this.startElement = startElement;
+ }
+
+ @Override
+ public String getNamespace(final String prefix) {
+ return startElement.getNamespaceContext().getNamespaceURI(prefix);
+ }
+ }
+
+ public static class PrefixMapingFromJson implements PrefixesMaping {
+
+ @Override
+ public String getNamespace(final String prefix) {
+ return prefix;
+ }
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 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.codec.gson.helpers;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Function;
+import com.google.common.base.Objects;
+import com.google.common.base.Preconditions;
+import com.google.common.base.Predicate;
+import com.google.common.collect.Iterables;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Set;
+
+import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceNode;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+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.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+
+/**
+ * This class is implementation-internal and subject to change. Please do not use it.
+ */
+@Beta
+public final class SchemaContextUtils {
+ private final SchemaContext schemaContext;
+
+ private SchemaContextUtils(final SchemaContext schemaContext) {
+ this.schemaContext = Preconditions.checkNotNull(schemaContext);
+ }
+
+ public static SchemaContextUtils create(final SchemaContext schemaContext) {
+ return new SchemaContextUtils(schemaContext);
+ }
+
+ public URI findNamespaceByModuleName(final String moduleName) {
+ final Module module = this.findModuleByName(moduleName);
+ return module == null ? null : module.getNamespace();
+ }
+
+
+ public Module findModuleByName(final String moduleName) {
+ checkPreconditions();
+ Preconditions.checkArgument(moduleName != null && !moduleName.isEmpty());
+ return schemaContext.findModuleByName(moduleName, null);
+ }
+
+ public Module findModuleByNamespace(final URI namespace) {
+ this.checkPreconditions();
+ Preconditions.checkArgument(namespace != null);
+ return schemaContext.findModuleByNamespaceAndRevision(namespace, null);
+ }
+
+ private void checkPreconditions() {
+ if (schemaContext == null) {
+ throw new IllegalStateException("Schema context isn't set.");
+ }
+ }
+
+ public DataSchemaNode findInstanceDataChildByNameAndNamespace(final DataNodeContainer container, final String name,
+ final URI namespace) {
+ Preconditions.<URI> checkNotNull(namespace);
+
+ final List<DataSchemaNode> potentialSchemaNodes = findInstanceDataChildrenByName(container, name);
+
+ Predicate<DataSchemaNode> filter = new Predicate<DataSchemaNode>() {
+ @Override
+ public boolean apply(final DataSchemaNode node) {
+ return Objects.equal(node.getQName().getNamespace(), namespace);
+ }
+ };
+
+ Iterable<DataSchemaNode> result = Iterables.filter(potentialSchemaNodes, filter);
+ return Iterables.getFirst(result, null);
+ }
+
+ public List<DataSchemaNode> findInstanceDataChildrenByName(final DataNodeContainer container, final String name) {
+ Preconditions.<DataNodeContainer> checkNotNull(container);
+ Preconditions.<String> checkNotNull(name);
+
+ List<DataSchemaNode> instantiatedDataNodeContainers = new ArrayList<DataSchemaNode>();
+ collectInstanceDataNodeContainers(instantiatedDataNodeContainers, container, name);
+ return instantiatedDataNodeContainers;
+ }
+
+ private void collectInstanceDataNodeContainers(final List<DataSchemaNode> potentialSchemaNodes,
+ final DataNodeContainer container, final String name) {
+
+ Predicate<DataSchemaNode> filter = new Predicate<DataSchemaNode>() {
+ @Override
+ public boolean apply(final DataSchemaNode node) {
+ return Objects.equal(node.getQName().getLocalName(), name);
+ }
+ };
+
+ Iterable<DataSchemaNode> nodes = Iterables.filter(container.getChildNodes(), filter);
+
+ // Can't combine this loop with the filter above because the filter is
+ // lazily-applied by Iterables.filter.
+ for (final DataSchemaNode potentialNode : nodes) {
+ if (isInstantiatedDataSchema(potentialNode)) {
+ potentialSchemaNodes.add(potentialNode);
+ }
+ }
+
+ Iterable<ChoiceNode> choiceNodes = Iterables.filter(container.getChildNodes(), ChoiceNode.class);
+ Iterable<Set<ChoiceCaseNode>> map = Iterables.transform(choiceNodes, CHOICE_FUNCTION);
+
+ final Iterable<ChoiceCaseNode> allCases = Iterables.<ChoiceCaseNode> concat(map);
+ for (final ChoiceCaseNode caze : allCases) {
+ collectInstanceDataNodeContainers(potentialSchemaNodes, caze, name);
+ }
+ }
+
+ public boolean isInstantiatedDataSchema(final DataSchemaNode node) {
+ return node instanceof LeafSchemaNode || node instanceof LeafListSchemaNode
+ || node instanceof ContainerSchemaNode || node instanceof ListSchemaNode
+ || node instanceof AnyXmlSchemaNode;
+ }
+
+ private final Function<ChoiceNode, Set<ChoiceCaseNode>> CHOICE_FUNCTION = new Function<ChoiceNode, Set<ChoiceCaseNode>>() {
+ @Override
+ public Set<ChoiceCaseNode> apply(final ChoiceNode node) {
+ return node.getCases();
+ }
+ };
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2014 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.codec.gson;
+
+import com.google.gson.stream.JsonReader;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.StringReader;
+import java.io.StringWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument;
+import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode;
+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.LoggingNormalizedNodeStreamWriter;
+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.data.impl.schema.Builders;
+import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter;
+import org.opendaylight.yangtools.yang.data.impl.schema.builder.api.NormalizedNodeContainerBuilder;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.parser.api.YangContextParser;
+import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class StreamToNormalizedNodeTest {
+ private static final Logger LOG = LoggerFactory.getLogger(StreamToNormalizedNodeTest.class);
+ private static SchemaContext schemaContext;
+ private static String streamAsString;
+
+ @BeforeClass
+ public static void initialization() throws IOException {
+ schemaContext = loadModules("/complexjson/yang");
+ streamAsString = loadTextFile(StreamToNormalizedNodeTest.class.getResource("/complexjson/complex-json.json")
+ .getPath());
+ }
+
+ /**
+ * Demonstrates how to log events produced by a {@link JsonReader}.
+ *
+ * @throws IOException
+ */
+ @Test
+ public void ownStreamWriterImplementationDemonstration() throws IOException {
+ // GSON's JsonReader reading from the loaded string (our event source)
+ final JsonReader reader = new JsonReader(new StringReader(streamAsString));
+
+ // StreamWriter which outputs SLF4J events
+ final LoggingNormalizedNodeStreamWriter logWriter = new LoggingNormalizedNodeStreamWriter();
+
+ // JSON -> StreamWriter parser
+ try (final JsonParserStream jsonHandler = JsonParserStream.create(logWriter, schemaContext)) {
+ // Process multiple readers, flush()/close() as needed
+ jsonHandler.parse(reader);
+ }
+ }
+
+ /**
+ * Demonstrates how to create an immutable NormalizedNode tree from a {@link JsonReader} and
+ * then writes the data back into string representation.
+ *
+ * @throws IOException
+ */
+ @Test
+ public void immutableNormalizedNodeStreamWriterDemonstration() throws IOException {
+ /*
+ * This is the parsing part
+ */
+ // This is where we will output the nodes
+ final NormalizedNodeContainerBuilder<NodeIdentifier, ?, ?, ? extends NormalizedNode<?, ?>> parent =
+ Builders.containerBuilder().withNodeIdentifier(new NodeIdentifier(QName.create("dummy", "2014-12-31", "dummy")));
+
+ // StreamWriter which attaches NormalizedNode under parent
+ final NormalizedNodeStreamWriter streamWriter = ImmutableNormalizedNodeStreamWriter.from(parent);
+
+ // JSON -> StreamWriter parser
+ try (JsonParserStream handler = JsonParserStream.create(streamWriter, schemaContext)) {
+ handler.parse(new JsonReader(new StringReader(streamAsString)));
+ }
+
+ // Finally build the node
+ final NormalizedNode<?, ?> parsedData = parent.build();
+ LOG.debug("Parsed NormalizedNodes: {}", parsedData);
+
+ /*
+ * This is the serialization part.
+ */
+ // We want to write the first child out
+ final DataContainerChild<? extends PathArgument, ?> firstChild = ((ContainerNode) parsedData).getValue().iterator().next();
+ LOG.debug("Serializing first child: {}", firstChild);
+
+ // String holder
+ final StringWriter writer = new StringWriter();
+
+ // StreamWriter which outputs JSON strings
+ final NormalizedNodeStreamWriter jsonStream = JSONNormalizedNodeStreamWriter.create(schemaContext, writer, 2);
+
+ // NormalizedNode -> StreamWriter
+ final NormalizedNodeWriter nodeWriter = NormalizedNodeWriter.forStreamWriter(jsonStream);
+
+ // Write multiple NormalizedNodes fluently, flush()/close() as needed
+ nodeWriter.write(firstChild).close();
+
+ // Just to put it somewhere
+ LOG.debug("Serialized JSON: {}", writer.toString());
+ }
+
+ private static SchemaContext loadModules(final String resourceDirectory) throws IOException {
+ YangContextParser parser = new YangParserImpl();
+ String path = StreamToNormalizedNodeTest.class.getResource(resourceDirectory).getPath();
+ final File testDir = new File(path);
+ final String[] fileList = testDir.list();
+ final List<File> testFiles = new ArrayList<File>();
+ if (fileList == null) {
+ throw new FileNotFoundException(resourceDirectory);
+ }
+ for (String fileName : fileList) {
+ if (new File(testDir, fileName).isDirectory() == false) {
+ testFiles.add(new File(testDir, fileName));
+ }
+ }
+ return parser.parseFiles(testFiles);
+ }
+
+ private static String loadTextFile(final String filePath) throws IOException {
+ FileReader fileReader = new FileReader(filePath);
+ BufferedReader bufReader = new BufferedReader(fileReader);
+
+ String line = null;
+ StringBuilder result = new StringBuilder();
+ while ((line = bufReader.readLine()) != null) {
+ result.append(line);
+ }
+ bufReader.close();
+ return result.toString();
+ }
+}
--- /dev/null
+{
+ "complexjson:cont1": {
+ "lf12-any":[
+ {
+ "anyxml-in-data":"foo"
+ }
+ ],
+
+ "lf13-any":{
+ "anyxml-in-data":"foo"
+ },
+
+ "lf14-any":"anyxml data",
+
+ "lflst11":["lflst11 value1","lflst11 value2"],
+
+ "lst11":[
+ {
+ "key111":"key111 value",
+ "lf112":"/complexjson:cont1/complexjson:lflst11",
+ "lf113":"lf113 value",
+ "lf111":"lf111 value"
+ }
+ ],
+ "lf11" : "453",
+ "lf12_1" : "lf12 value",
+ "lf13" : "lf13 value",
+ "complexjson-augmentation:lf15_11" : "lf15_11 value from augmentation",
+ "complexjson-augmentation:lf15_12" : "lf15_12 value from augmentation",
+ "lf15_11" : "one two",
+ "lf15_12" : "complexjson:lf11",
+ "lf15_21" : "lf15_21 value",
+ "lf17" : "lf17 value",
+
+ "lst12":[
+ {
+ "lf121":"lf121 value"
+ }
+ ]
+ }
+}
\ No newline at end of file
--- /dev/null
+module complexjson-augmentation {
+ namespace "ns:complex:json:augmentation";
+ prefix cjaug;
+
+ import complexjson {
+ prefix cj;
+ }
+
+ revision "2014-08-14" {
+ }
+
+ augment "/cj:cont1/cj:choc11/cj:c11A" {
+ leaf lf15_11 {
+ type string;
+ }
+ leaf lf15_12 {
+ type string;
+ }
+
+ }
+
+ augment "/cj:cont1" {
+ leaf lf12_1aug {
+ type string;
+ }
+ leaf lf12_2aug {
+ type string;
+ }
+ }
+
+ augment "/cj:cont1/cj:choc11/cj:c11A" {
+ leaf lf15_21aug {
+ type string;
+ }
+ }
+
+}
--- /dev/null
+module complexjson {
+ namespace "ns:complex:json";
+ prefix cj;
+
+ revision "2014-08-11" {
+ }
+
+
+ identity ident;
+
+ container cont1 {
+
+ anyxml lf12-any;
+ anyxml lf13-any;
+ anyxml lf14-any;
+
+ leaf lf11 {
+ type int32;
+ }
+
+ leaf-list lflst11 {
+ type string;
+ }
+
+ list lst11 {
+ key "key111 lf111";
+ leaf key111 {
+ type string;
+ }
+ leaf lf111 {
+ type string;
+ }
+ leaf lf112 {
+ type instance-identifier;
+ }
+ leaf lf113 {
+ type string;
+ }
+ }
+
+ list lst12 {
+ leaf lf121 {
+ type string;
+ }
+ leaf lf122 {
+ type string;
+ }
+ }
+
+
+ choice choc11 {
+ case c11A {
+ leaf lf13 {
+ type string;
+ }
+ }
+ leaf lf16 {
+ type string;
+ }
+ }
+
+ choice choc12 {
+ case c12A {
+ }
+ }
+ }
+
+
+ augment "/cont1/choc12" {
+ case c12B {
+ leaf lf17 {
+ type string;
+ }
+ }
+ }
+
+
+ augment "/cont1" {
+ container cont11 {
+ leaf lf111 {
+ type string;
+ }
+ }
+ }
+
+ augment "/cont1" {
+ leaf lf12_1 {
+ type string;
+ }
+ leaf lf12_2 {
+ type string;
+ }
+ }
+
+ augment "/cont1" {
+ leaf lf12_3 {
+ type string;
+ }
+ }
+
+
+ augment "/cont1/choc11" {
+ case c11B {
+ leaf lf14_1 {
+ type string;
+ }
+ }
+ }
+
+ augment "/cont1/choc11" {
+ case c11C {
+ leaf lf14_2 {
+ type string;
+ }
+ }
+ }
+
+ augment "/cont1/choc11/c11A" {
+ leaf lf15_11 {
+ type bits {
+ bit one;
+ bit two;
+ bit three;
+ }
+ }
+ leaf lf15_12 {
+ type identityref {
+ base ident;
+ }
+ }
+
+ }
+
+ augment "/cont1/choc11/c11A" {
+ leaf lf15_21 {
+ type string;
+ }
+ }
+
+}