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.gson.JsonIOException;
import com.google.gson.JsonParseException;
import com.google.gson.JsonSyntaxException;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.MalformedJsonException;
-
import java.io.Closeable;
import java.io.EOFException;
import java.io.Flushable;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collections;
import java.util.Deque;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
-
import org.opendaylight.yangtools.yang.common.QName;
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.LeafListSchemaNode;
import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.opendaylight.yangtools.yang.model.api.SchemaNode;
import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
}
public static JsonParserStream create(final NormalizedNodeStreamWriter writer, final SchemaContext schemaContext, final SchemaNode parentNode ) {
+ if(parentNode instanceof RpcDefinition) {
+ return new JsonParserStream(writer, schemaContext, new RpcAsContainer((RpcDefinition) parentNode));
+ }
Preconditions.checkArgument(parentNode instanceof DataSchemaNode, "Instance of DataSchemaNode class awaited.");
return new JsonParserStream(writer, schemaContext, (DataSchemaNode) parentNode);
}
public JsonParserStream parse(final JsonReader reader) throws JsonIOException, JsonSyntaxException {
// code copied from gson's JsonParser and Stream classes
- boolean lenient = reader.isLenient();
+ final boolean lenient = reader.isLenient();
reader.setLenient(true);
boolean isEmpty = true;
try {
reader.peek();
isEmpty = false;
- CompositeNodeDataWithSchema compositeNodeDataWithSchema = new CompositeNodeDataWithSchema(parentNode);
+ final CompositeNodeDataWithSchema compositeNodeDataWithSchema = new CompositeNodeDataWithSchema(parentNode);
read(reader, compositeNodeDataWithSchema);
compositeNodeDataWithSchema.write(writer);
return this;
// return read(reader);
- } catch (EOFException e) {
+ } catch (final 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) {
+ } catch (final MalformedJsonException e) {
throw new JsonSyntaxException(e);
- } catch (IOException e) {
+ } catch (final IOException e) {
throw new JsonIOException(e);
- } catch (NumberFormatException e) {
+ } catch (final NumberFormatException e) {
throw new JsonSyntaxException(e);
} catch (StackOverflowError | OutOfMemoryError e) {
throw new JsonParseException("Failed parsing JSON source: " + reader + " to Json", e);
in.endArray();
return;
case BEGIN_OBJECT:
- Set<String> namesakes = new HashSet<>();
+ final Set<String> namesakes = new HashSet<>();
in.beginObject();
while (in.hasNext()) {
final String jsonElementName = in.nextName();
- final NamespaceAndName namespaceAndName = resolveNamespace(jsonElementName);
+ final NamespaceAndName namespaceAndName = resolveNamespace(jsonElementName, parent.getSchema());
final String localName = namespaceAndName.getName();
addNamespace(namespaceAndName.getUri());
if (namesakes.contains(jsonElementName)) {
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 void addNamespace(final URI namespace) {
+ namespaces.push(namespace);
}
- private NamespaceAndName resolveNamespace(final String childName) {
- int lastIndexOfColon = childName.lastIndexOf(':');
+ private NamespaceAndName resolveNamespace(final String childName, final DataSchemaNode dataSchemaNode) {
+ final int lastIndexOfColon = childName.lastIndexOf(':');
String moduleNamePart = null;
String nodeNamePart = null;
URI namespace = null;
nodeNamePart = childName;
}
- Optional<URI> namespaceOpt = namespace == null ? Optional.<URI> absent() : Optional.of(namespace);
- return new NamespaceAndName(nodeNamePart, namespaceOpt);
+ if (namespace == null) {
+ Set<URI> potentialUris = Collections.emptySet();
+ potentialUris = resolveAllPotentialNamespaces(nodeNamePart, dataSchemaNode);
+ if (potentialUris.contains(getCurrentNamespace())) {
+ namespace = getCurrentNamespace();
+ } else if (potentialUris.size() == 1) {
+ namespace = potentialUris.iterator().next();
+ } else if (potentialUris.size() > 1) {
+ throw new IllegalStateException("Choose suitable module name for element "+nodeNamePart+":"+toModuleNames(potentialUris));
+ } else if (potentialUris.isEmpty()) {
+ throw new IllegalStateException("Schema node with name "+nodeNamePart+" wasn't found.");
+ }
+ }
+
+ return new NamespaceAndName(nodeNamePart, namespace);
+ }
+
+ private String toModuleNames(final Set<URI> potentialUris) {
+ final StringBuilder builder = new StringBuilder();
+ for (final URI potentialUri : potentialUris) {
+ builder.append("\n");
+ //FIXME how to get information about revision from JSON input? currently first available is used.
+ builder.append(schema.findModuleByNamespace(potentialUri).iterator().next().getName());
+ }
+ return builder.toString();
+ }
+
+ private Set<URI> resolveAllPotentialNamespaces(final String elementName, final DataSchemaNode dataSchemaNode) {
+ final Set<URI> potentialUris = new HashSet<>();
+ final Set<ChoiceNode> choices = new HashSet<>();
+ if (dataSchemaNode instanceof DataNodeContainer) {
+ for (final DataSchemaNode childSchemaNode : ((DataNodeContainer) dataSchemaNode).getChildNodes()) {
+ if (childSchemaNode instanceof ChoiceNode) {
+ choices.add((ChoiceNode)childSchemaNode);
+ } else if (childSchemaNode.getQName().getLocalName().equals(elementName)) {
+ potentialUris.add(childSchemaNode.getQName().getNamespace());
+ }
+ }
+
+ for (final ChoiceNode choiceNode : choices) {
+ for (final ChoiceCaseNode concreteCase : choiceNode.getCases()) {
+ potentialUris.addAll(resolveAllPotentialNamespaces(elementName, concreteCase));
+ }
+ }
+ }
+ return potentialUris;
}
private URI getCurrentNamespace() {
private Deque<DataSchemaNode> findSchemaNodeByNameAndNamespace(final DataSchemaNode dataSchemaNode,
final String childName, final URI namespace) {
final Deque<DataSchemaNode> result = new ArrayDeque<>();
- List<ChoiceNode> childChoices = new ArrayList<>();
+ final List<ChoiceNode> childChoices = new ArrayList<>();
if (dataSchemaNode instanceof DataNodeContainer) {
- for (DataSchemaNode childNode : ((DataNodeContainer) dataSchemaNode).getChildNodes()) {
+ for (final DataSchemaNode childNode : ((DataNodeContainer) dataSchemaNode).getChildNodes()) {
if (childNode instanceof ChoiceNode) {
childChoices.add((ChoiceNode) childNode);
} else {
}
}
// 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,
+ for (final ChoiceNode choiceNode : childChoices) {
+ for (final ChoiceCaseNode concreteCase : choiceNode.getCases()) {
+ final Deque<DataSchemaNode> resultFromRecursion = findSchemaNodeByNameAndNamespace(concreteCase, childName,
namespace);
if (!resultFromRecursion.isEmpty()) {
resultFromRecursion.push(concreteCase);
}
private static class NamespaceAndName {
- private final Optional<URI> uri;
+ private final URI uri;
private final String name;
- public NamespaceAndName(final String name, final Optional<URI> uri) {
+ public NamespaceAndName(final String name, final URI uri) {
this.name = name;
this.uri = uri;
}
return name;
}
- public Optional<URI> getUri() {
+ public URI getUri() {
return uri;
}
}
--- /dev/null
+/*
+ * Copyright (c) 2015 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.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.AugmentationSchema;
+import org.opendaylight.yangtools.yang.model.api.ConstraintDefinition;
+import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.GroupingDefinition;
+import org.opendaylight.yangtools.yang.model.api.RpcDefinition;
+import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.Status;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
+import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.UsesNode;
+
+final class RpcAsContainer implements ContainerSchemaNode {
+
+ private final RpcDefinition delegate;
+
+ @Override
+ public String getDescription() {
+ return delegate.getDescription();
+ }
+
+ @Override
+ public String getReference() {
+ return delegate.getReference();
+ }
+
+ @Override
+ public Set<TypeDefinition<?>> getTypeDefinitions() {
+ return delegate.getTypeDefinitions();
+ }
+
+ @Override
+ public Set<GroupingDefinition> getGroupings() {
+ return delegate.getGroupings();
+ }
+
+ @Override
+ public Status getStatus() {
+ return delegate.getStatus();
+ }
+
+ public ContainerSchemaNode getInput() {
+ return delegate.getInput();
+ }
+
+ public ContainerSchemaNode getOutput() {
+ return delegate.getOutput();
+ }
+
+ RpcAsContainer(final RpcDefinition parentNode) {
+ delegate = parentNode;
+ }
+
+ @Override
+ public QName getQName() {
+ return delegate.getQName();
+ }
+
+ @Override
+ public SchemaPath getPath() {
+ return delegate.getPath();
+ }
+
+ @Override
+ public List<UnknownSchemaNode> getUnknownSchemaNodes() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public DataSchemaNode getDataChildByName(final QName name) {
+ return getDataChildByName(name.getLocalName());
+ }
+
+ @Override
+ public DataSchemaNode getDataChildByName(final String name) {
+ switch (name) {
+ case "input":
+ return delegate.getInput();
+ case "output":
+ return delegate.getOutput();
+ }
+ return null;
+ }
+
+ @Override
+ public Set<UsesNode> getUses() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public Set<AugmentationSchema> getAvailableAugmentations() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public boolean isPresenceContainer() {
+ return false;
+ }
+
+ @Override
+ public Collection<DataSchemaNode> getChildNodes() {
+ final ArrayList<DataSchemaNode> ret = new ArrayList<>();
+ final ContainerSchemaNode input = getInput();
+ final ContainerSchemaNode output = getOutput();
+ if(input != null) {
+ ret.add(input);
+ }
+ if(output != null) {
+ ret.add(output);
+ }
+ return ret;
+ }
+
+ @Override
+ public boolean isAugmenting() {
+ return false;
+ }
+
+ @Override
+ public boolean isAddedByUses() {
+ return false;
+ }
+
+ @Override
+ public boolean isConfiguration() {
+ return false;
+ }
+
+ @Override
+ public ConstraintDefinition getConstraints() {
+ // TODO Auto-generated method stub
+ return null;
+ }
+
+}
package org.opendaylight.yangtools.yang.data.codec.gson;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
import static org.opendaylight.yangtools.yang.data.codec.gson.TestUtils.loadModules;
import static org.opendaylight.yangtools.yang.data.codec.gson.TestUtils.loadTextFile;
verifyTransformationToNormalizedNode(inputJson, TestingNormalizedNodeStructuresCreator.unkeyedNodeInContainer());
}
+ /**
+ * Top level JSON element contains no information about module name.
+ *
+ * It should be possible to find out potential module name from available schema context.
+ *
+ */
+ @Test
+ public void missingModuleInfoInTopLevelElement() throws IOException, URISyntaxException {
+ String inputJson = loadTextFile("/complexjson/missing-module-in-top-level.json");
+ verifyTransformationToNormalizedNode(inputJson, TestingNormalizedNodeStructuresCreator.topLevelContainer());
+ }
+
+ /**
+ *
+ * Exception expected.
+ *
+ * It tests case when several elements with the same name and various namespaces exists and are in JSON specified
+ * without module name prefix.
+ */
+ @Test
+ public void leafNamesakes() throws IOException, URISyntaxException {
+ String inputJson = loadTextFile("/complexjson/namesakes.json");
+ try {
+ //second parameter isn't necessary because error will be raised before it is used.
+ verifyTransformationToNormalizedNode(inputJson, null);
+ } catch (IllegalStateException e) {
+ final String errorMessage = e.getMessage();
+ assertTrue(errorMessage.contains("Choose suitable module name for element lf11-namesake:"));
+ assertTrue(errorMessage.contains("complexjson-augmentation"));
+ assertTrue(errorMessage.contains("complexjson-augmentation-namesake"));
+ }
+ }
+
+ /**
+ *
+ * Exception expected.
+ *
+ * Json input contains element which doesn't exist in YANG schema
+ */
+ @Test
+ public void parsingNotExistingElement() throws IOException, URISyntaxException {
+ String inputJson = loadTextFile("/complexjson/not-existing-element.json");
+ try {
+ //second parameter isn't necessary because error will be raised before it is used.
+ verifyTransformationToNormalizedNode(inputJson, null);
+ } catch (IllegalStateException e) {
+ assertTrue(e.getMessage().contains("Schema node with name dummy-element wasn't found."));
+ }
+ }
+
+
private void verifyTransformationToNormalizedNode(final String inputJson,
final NormalizedNode<?, ?> awaitedStructure) {
NormalizedNodeResult result = new NormalizedNodeResult();