X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=blobdiff_plain;f=yang%2Fyang-data-codec-gson%2Fsrc%2Fmain%2Fjava%2Forg%2Fopendaylight%2Fyangtools%2Fyang%2Fdata%2Fcodec%2Fgson%2FJsonParserStream.java;h=9a93146e21fb4a5886cdf954e70c2bf6559045bb;hb=b52c1ffb0df2b84665b4d222166a3e4cdb8427bb;hp=f56d6d2344880a6aa676be7230930be41521256d;hpb=8d0aee96d710d3148c3a2a36881312046bd01b96;p=yangtools.git diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JsonParserStream.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JsonParserStream.java index f56d6d2344..9a93146e21 100644 --- a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JsonParserStream.java +++ b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JsonParserStream.java @@ -21,26 +21,25 @@ import java.io.IOException; import java.net.URI; 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.DataSchemaNodeAwareAdaptor; import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter; +import org.opendaylight.yangtools.yang.data.api.schema.stream.SchemaAwareNormalizedNodeStreamWriter; import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode; import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode; import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode; import org.opendaylight.yangtools.yang.model.api.DataNodeContainer; import org.opendaylight.yangtools.yang.model.api.DataSchemaNode; -import org.opendaylight.yangtools.yang.model.api.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; +import org.opendaylight.yangtools.yang.model.api.YangModeledAnyXmlSchemaNode; /** * This class parses JSON elements from a GSON JsonReader. It disallows multiple elements of the same name unlike the @@ -49,20 +48,22 @@ import org.opendaylight.yangtools.yang.model.api.TypeDefinition; @Beta public final class JsonParserStream implements Closeable, Flushable { private final Deque namespaces = new ArrayDeque<>(); - private final NormalizedNodeStreamWriter writer; + private final SchemaAwareNormalizedNodeStreamWriter writer; private final JSONCodecFactory codecs; private final SchemaContext schema; private final DataSchemaNode parentNode; - private JsonParserStream(final NormalizedNodeStreamWriter writer, final SchemaContext schemaContext, final DataSchemaNode parentNode) { + private JsonParserStream(final NormalizedNodeStreamWriter writer, final SchemaContext schemaContext, + final DataSchemaNode parentNode) { this.schema = Preconditions.checkNotNull(schemaContext); - this.writer = Preconditions.checkNotNull(writer); + this.writer = DataSchemaNodeAwareAdaptor.forWriter(writer); this.codecs = JSONCodecFactory.create(schemaContext); this.parentNode = parentNode; } - public static JsonParserStream create(final NormalizedNodeStreamWriter writer, final SchemaContext schemaContext, final SchemaNode parentNode ) { - if(parentNode instanceof RpcDefinition) { + 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."); @@ -73,7 +74,7 @@ public final class JsonParserStream implements Closeable, Flushable { return new JsonParserStream(writer, schemaContext, schemaContext); } - public JsonParserStream parse(final JsonReader reader) throws JsonIOException, JsonSyntaxException { + public JsonParserStream parse(final JsonReader reader) { // code copied from gson's JsonParser and Stream classes final boolean lenient = reader.isLenient(); @@ -83,15 +84,13 @@ public final class JsonParserStream implements Closeable, Flushable { reader.peek(); isEmpty = false; final CompositeNodeDataWithSchema compositeNodeDataWithSchema = new CompositeNodeDataWithSchema(parentNode); - read(reader, compositeNodeDataWithSchema,true); + read(reader, compositeNodeDataWithSchema); compositeNodeDataWithSchema.write(writer); return this; - // return read(reader); } 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); @@ -108,14 +107,18 @@ public final class JsonParserStream implements Closeable, Flushable { } } - private final void setValue(final AbstractNodeDataWithSchema parent, final String value) { - Preconditions.checkArgument(parent instanceof SimpleNodeDataWithSchema, "Node %s is not a simple type", parent); + private void setValue(final AbstractNodeDataWithSchema parent, final String value) { + Preconditions.checkArgument(parent instanceof SimpleNodeDataWithSchema, "Node %s is not a simple type", + parent.getSchema().getQName()); + final SimpleNodeDataWithSchema parentSimpleNode = (SimpleNodeDataWithSchema) parent; + Preconditions.checkArgument(parentSimpleNode.getValue() == null, "Node '%s' has already set its value to '%s'", + parentSimpleNode.getSchema().getQName(), parentSimpleNode.getValue()); - final Object translatedValue = translateValueByType(value, parent.getSchema()); - ((SimpleNodeDataWithSchema) parent).setValue(translatedValue); + final Object translatedValue = translateValueByType(value, parentSimpleNode.getSchema()); + parentSimpleNode.setValue(translatedValue); } - public void read(final JsonReader in, final AbstractNodeDataWithSchema parent, final boolean rootRead) throws IOException { + public void read(final JsonReader in, AbstractNodeDataWithSchema parent) throws IOException { switch (in.peek()) { case STRING: case NUMBER: @@ -131,44 +134,59 @@ public final class JsonParserStream implements Closeable, Flushable { 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); + if (parent instanceof LeafNodeDataWithSchema) { + read(in, parent); + } else { + final AbstractNodeDataWithSchema newChild = newArrayEntry(parent); + read(in, newChild); } - read(in, newChild,false); } in.endArray(); return; case BEGIN_OBJECT: final Set namesakes = new HashSet<>(); in.beginObject(); + /* + * This allows parsing of incorrectly /as showcased/ + * in testconf nesting of list items - eg. + * lists with one value are sometimes serialized + * without wrapping array. + * + */ + if(isArray(parent)) { + parent = newArrayEntry(parent); + } while (in.hasNext()) { final String jsonElementName = in.nextName(); - final NamespaceAndName namespaceAndName = resolveNamespace(jsonElementName, parent.getSchema()); + DataSchemaNode parentSchema = parent.getSchema(); + if (parentSchema instanceof YangModeledAnyXmlSchemaNode) { + parentSchema = ((YangModeledAnyXmlSchemaNode) parentSchema).getSchemaOfAnyXmlData(); + } + final NamespaceAndName namespaceAndName = resolveNamespace(jsonElementName, parentSchema); 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 childDataSchemaNodes = findSchemaNodeByNameAndNamespace(parent.getSchema(), + + final Deque childDataSchemaNodes = findSchemaNodeByNameAndNamespace(parentSchema, 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,rootRead); -// FIXME:anyxml data shouldn't be skipped but should be loaded somehow. will be specified after 17AUG2014 + final AbstractNodeDataWithSchema newChild = ((CompositeNodeDataWithSchema) parent).addChild(childDataSchemaNodes); + /* + * FIXME:anyxml data shouldn't be skipped but should be loaded somehow. + * will be able to load anyxml which conforms to YANG data using these + * parser, for other anyxml will be harder. + */ if (newChild instanceof AnyXmlNodeDataWithSchema) { in.skipValue(); } else { - read(in, newChild,false); + read(in, newChild); } removeNamespace(); } @@ -182,33 +200,33 @@ public final class JsonParserStream implements Closeable, Flushable { } } - private Object translateValueByType(final String value, final DataSchemaNode node) { - final TypeDefinition typeDefinition = typeDefinition(node); - if (typeDefinition == null) { - return value; - } - - return codecs.codecFor(typeDefinition).deserialize(value); + private static boolean isArray(final AbstractNodeDataWithSchema parent) { + return parent instanceof ListNodeDataWithSchema || parent instanceof LeafListNodeDataWithSchema; } - private static TypeDefinition 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; + private static AbstractNodeDataWithSchema newArrayEntry(final AbstractNodeDataWithSchema parent) { + AbstractNodeDataWithSchema newChild; + if (parent instanceof ListNodeDataWithSchema) { + newChild = new ListEntryNodeDataWithSchema(parent.getSchema()); + } else if (parent instanceof LeafListNodeDataWithSchema) { + newChild = new LeafListEntryNodeDataWithSchema(parent.getSchema()); } else { - throw new IllegalArgumentException("Unhandled parameter types: " + Arrays. asList(node).toString()); + throw new IllegalStateException("Found an unexpected array nested under "+ parent.getSchema().getQName()); } + ((CompositeNodeDataWithSchema) parent).addChild(newChild); + return newChild; + } - if (baseType != null) { - while (baseType.getBaseType() != null) { - baseType = baseType.getBaseType(); - } + private Object translateValueByType(final String value, final DataSchemaNode node) { + if (node instanceof AnyXmlSchemaNode) { + /* + * FIXME: Figure out some YANG extension dispatch, which will + * reuse JSON parsing or XML parsing - anyxml is not well-defined in + * JSON. + */ + return value; } - return baseType; + return codecs.codecFor(node).deserialize(value); } private void removeNamespace() { @@ -244,7 +262,7 @@ public final class JsonParserStream implements Closeable, Flushable { } 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."); + throw new IllegalStateException("Schema node with name "+nodeNamePart+" wasn't found under "+dataSchemaNode.getQName()+"."); } } @@ -301,19 +319,28 @@ public final class JsonParserStream implements Closeable, Flushable { final String childName, final URI namespace) { final Deque result = new ArrayDeque<>(); final List childChoices = new ArrayList<>(); + DataSchemaNode potentialChildNode = null; if (dataSchemaNode instanceof DataNodeContainer) { for (final DataSchemaNode childNode : ((DataNodeContainer) dataSchemaNode).getChildNodes()) { if (childNode instanceof ChoiceSchemaNode) { childChoices.add((ChoiceSchemaNode) childNode); } else { final QName childQName = childNode.getQName(); + if (childQName.getLocalName().equals(childName) && childQName.getNamespace().equals(namespace)) { - result.push(childNode); - return result; + if (potentialChildNode == null || + childQName.getRevision().after(potentialChildNode.getQName().getRevision())) { + potentialChildNode = childNode; + } } } } } + if (potentialChildNode != null) { + result.push(potentialChildNode); + return result; + } + // try to find data schema node in choice (looking for first match) for (final ChoiceSchemaNode choiceNode : childChoices) { for (final ChoiceCaseNode concreteCase : choiceNode.getCases()) {