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=f56d6d2344880a6aa676be7230930be41521256d;hb=244670baeb9d085afacba4426a7336280a9a8140;hp=20e53023942d240720cd07f9fac413d8d4013791;hpb=bc7eb45a5012be0bbc0e9f67a5991856560c28d5;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 20e5302394..f56d6d2344 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 @@ -8,47 +8,39 @@ 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.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.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.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.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.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 @@ -58,48 +50,56 @@ import org.opendaylight.yangtools.yang.model.api.type.InstanceIdentifierTypeDefi public final class JsonParserStream implements Closeable, Flushable { private final Deque namespaces = new ArrayDeque<>(); private final NormalizedNodeStreamWriter writer; - private final SchemaContextUtils utils; - private final RestCodecFactory codecs; + private final JSONCodecFactory codecs; private final SchemaContext schema; + private final DataSchemaNode parentNode; - private JsonParserStream(final NormalizedNodeStreamWriter writer, final SchemaContext schemaContext) { + private JsonParserStream(final NormalizedNodeStreamWriter writer, final SchemaContext schemaContext, final DataSchemaNode parentNode) { this.schema = Preconditions.checkNotNull(schemaContext); - this.utils = SchemaContextUtils.create(schemaContext); this.writer = Preconditions.checkNotNull(writer); - this.codecs = RestCodecFactory.create(utils); + 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) { + 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 static JsonParserStream create(final NormalizedNodeStreamWriter writer, final SchemaContext schemaContext) { - return new JsonParserStream(writer, schemaContext); + return new JsonParserStream(writer, schemaContext, schemaContext); } 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(schema); - read(reader, compositeNodeDataWithSchema); - compositeNodeDataWithSchema.writeToStream(writer); + final CompositeNodeDataWithSchema compositeNodeDataWithSchema = new CompositeNodeDataWithSchema(parentNode); + read(reader, compositeNodeDataWithSchema,true); + 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); @@ -108,31 +108,26 @@ public final class JsonParserStream implements Closeable, Flushable { } } - public void read(final JsonReader in, final AbstractNodeDataWithSchema parent) throws IOException { + private final void setValue(final AbstractNodeDataWithSchema parent, final String value) { + Preconditions.checkArgument(parent instanceof SimpleNodeDataWithSchema, "Node %s is not a simple type", parent); + + final Object translatedValue = translateValueByType(value, parent.getSchema()); + ((SimpleNodeDataWithSchema) parent).setValue(translatedValue); + } - final JsonToken peek = in.peek(); - Optional value = Optional.absent(); - switch (peek) { + public void read(final JsonReader in, final AbstractNodeDataWithSchema parent, final boolean rootRead) throws IOException { + switch (in.peek()) { case STRING: case NUMBER: - value = Optional.of(in.nextString()); + setValue(parent, in.nextString()); break; case BOOLEAN: - value = Optional.of(Boolean.toString(in.nextBoolean())); + setValue(parent, Boolean.toString(in.nextBoolean())); break; case NULL: in.nextNull(); - value = Optional.of((String) null); + setValue(parent, 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()) { @@ -144,16 +139,16 @@ public final class JsonParserStream implements Closeable, Flushable { newChild = new LeafListEntryNodeDataWithSchema(parent.getSchema()); ((CompositeNodeDataWithSchema) parent).addChild(newChild); } - read(in, newChild); + read(in, newChild,false); } in.endArray(); return; case BEGIN_OBJECT: - Set namesakes = new HashSet<>(); + final Set 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)) { @@ -168,12 +163,12 @@ public final class JsonParserStream implements Closeable, Flushable { } AbstractNodeDataWithSchema newChild; - newChild = ((CompositeNodeDataWithSchema) parent).addChild(childDataSchemaNodes); + newChild = ((CompositeNodeDataWithSchema) parent).addChild(childDataSchemaNodes,rootRead); // 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); + read(in, newChild,false); } removeNamespace(); } @@ -183,6 +178,7 @@ public final class JsonParserStream implements Closeable, Flushable { case NAME: case END_OBJECT: case END_ARRAY: + break; } } @@ -192,16 +188,7 @@ public final class JsonParserStream implements Closeable, Flushable { return value; } - final Object inputValue; - if (typeDefinition instanceof IdentityrefTypeDefinition) { - inputValue = valueAsIdentityRef(value); - } else if (typeDefinition instanceof InstanceIdentifierTypeDefinition) { - inputValue = valueAsInstanceIdentifier(value); - } else { - inputValue = value; - } - - return codecs.codecFor(typeDefinition).deserialize(inputValue); + return codecs.codecFor(typeDefinition).deserialize(value); } private static TypeDefinition typeDefinition(final DataSchemaNode node) { @@ -224,78 +211,75 @@ public final class JsonParserStream implements Closeable, Flushable { 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) { - // The string needs to me in form "moduleName:localName" - final int idx = jsonElementName.indexOf(':'); - if (idx == -1 || jsonElementName.indexOf(':', idx + 1) != -1) { - return null; - } - - // FIXME: is this correct? This should be looking up module name instead - return URI.create(jsonElementName.substring(0, idx)); - } - - private static String getLocalNameFor(final String jsonElementName) { - // The string needs to me in form "moduleName:localName" - final int idx = jsonElementName.indexOf(':'); - if (idx == -1 || jsonElementName.indexOf(':', idx + 1) != -1) { - return jsonElementName; - } - - return jsonElementName.substring(idx + 1); - } - private void removeNamespace() { namespaces.pop(); } - private void addNamespace(final Optional 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; if (lastIndexOfColon != -1) { moduleNamePart = childName.substring(0, lastIndexOfColon); nodeNamePart = childName.substring(lastIndexOfColon + 1); - namespace = utils.findNamespaceByModuleName(moduleNamePart); + + final Module m = schema.findModuleByName(moduleNamePart, null); + namespace = m == null ? null : m.getNamespace(); } else { nodeNamePart = childName; } - Optional namespaceOpt = namespace == null ? Optional. absent() : Optional.of(namespace); - return new NamespaceAndName(nodeNamePart, namespaceOpt); + if (namespace == null) { + Set 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 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 resolveAllPotentialNamespaces(final String elementName, final DataSchemaNode dataSchemaNode) { + final Set potentialUris = new HashSet<>(); + final Set choices = new HashSet<>(); + if (dataSchemaNode instanceof DataNodeContainer) { + for (final DataSchemaNode childSchemaNode : ((DataNodeContainer) dataSchemaNode).getChildNodes()) { + if (childSchemaNode instanceof ChoiceSchemaNode) { + choices.add((ChoiceSchemaNode)childSchemaNode); + } else if (childSchemaNode.getQName().getLocalName().equals(elementName)) { + potentialUris.add(childSchemaNode.getQName().getNamespace()); + } + } + + for (final ChoiceSchemaNode choiceNode : choices) { + for (final ChoiceCaseNode concreteCase : choiceNode.getCases()) { + potentialUris.addAll(resolveAllPotentialNamespaces(elementName, concreteCase)); + } + } + } + return potentialUris; } private URI getCurrentNamespace() { @@ -316,11 +300,11 @@ public final class JsonParserStream implements Closeable, Flushable { private Deque findSchemaNodeByNameAndNamespace(final DataSchemaNode dataSchemaNode, final String childName, final URI namespace) { final Deque result = new ArrayDeque<>(); - List childChoices = new ArrayList<>(); + final List childChoices = new ArrayList<>(); if (dataSchemaNode instanceof DataNodeContainer) { - for (DataSchemaNode childNode : ((DataNodeContainer) dataSchemaNode).getChildNodes()) { - if (childNode instanceof ChoiceNode) { - childChoices.add((ChoiceNode) childNode); + 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)) { @@ -331,9 +315,9 @@ public final class JsonParserStream implements Closeable, Flushable { } } // try to find data schema node in choice (looking for first match) - for (ChoiceNode choiceNode : childChoices) { - for (ChoiceCaseNode concreteCase : choiceNode.getCases()) { - Deque resultFromRecursion = findSchemaNodeByNameAndNamespace(concreteCase, childName, + for (final ChoiceSchemaNode choiceNode : childChoices) { + for (final ChoiceCaseNode concreteCase : choiceNode.getCases()) { + final Deque resultFromRecursion = findSchemaNodeByNameAndNamespace(concreteCase, childName, namespace); if (!resultFromRecursion.isEmpty()) { resultFromRecursion.push(concreteCase); @@ -346,10 +330,10 @@ public final class JsonParserStream implements Closeable, Flushable { } private static class NamespaceAndName { - private final Optional uri; + private final URI uri; private final String name; - public NamespaceAndName(final String name, final Optional uri) { + public NamespaceAndName(final String name, final URI uri) { this.name = name; this.uri = uri; } @@ -358,7 +342,7 @@ public final class JsonParserStream implements Closeable, Flushable { return name; } - public Optional getUri() { + public URI getUri() { return uri; } }