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%2FJSONNormalizedNodeStreamWriter.java;h=0a8f9877e992d0f700cd62c68bd102a063595a94;hb=2c1d8cbaeacc2a688db2034329bd972a3ef69e48;hp=e1bba78be001caa2977611544256ff23e5ffd510;hpb=3b90e6cea3b8c8982e48546829ceadcd5f2cfb93;p=yangtools.git diff --git a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONNormalizedNodeStreamWriter.java b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONNormalizedNodeStreamWriter.java index e1bba78be0..0a8f9877e9 100644 --- a/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONNormalizedNodeStreamWriter.java +++ b/yang/yang-data-codec-gson/src/main/java/org/opendaylight/yangtools/yang/data/codec/gson/JSONNormalizedNodeStreamWriter.java @@ -7,136 +7,94 @@ */ package org.opendaylight.yangtools.yang.data.codec.gson; -import com.google.common.base.CharMatcher; import com.google.common.base.Preconditions; -import com.google.common.base.Strings; import com.google.gson.stream.JsonWriter; +import java.io.IOException; +import java.net.URI; 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.data.impl.codec.SchemaTracker; import org.opendaylight.yangtools.yang.model.api.AnyXmlSchemaNode; -import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; 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.SchemaNode; import org.opendaylight.yangtools.yang.model.api.SchemaPath; -import java.io.IOException; -import java.io.Writer; -import java.net.URI; - /** * 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 { +public final class JSONNormalizedNodeStreamWriter implements NormalizedNodeStreamWriter { /** * RFC6020 deviation: we are not required to emit empty containers unless they * are marked as 'presence'. */ private static final boolean DEFAULT_EMIT_EMPTY_CONTAINERS = true; - /** - * Matcher used to check if a string needs to be escaped. - */ - private static final CharMatcher JSON_ILLEGAL_STRING_CHARACTERS = CharMatcher.anyOf("\\\"\n\r"); - private final SchemaTracker tracker; private final JSONCodecFactory codecs; - private final Writer writer; - private final String indent; + private final JsonWriter writer; private JSONStreamWriterContext context; - private JSONNormalizedNodeStreamWriter(final JSONCodecFactory codecFactory, final SchemaPath path, - final Writer writer, final URI initialNs, final int indentSize) { - 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; - } + private JSONNormalizedNodeStreamWriter(final JSONCodecFactory codecFactory, final SchemaPath path, final JsonWriter JsonWriter, final JSONStreamWriterRootContext rootContext) { + this.writer = Preconditions.checkNotNull(JsonWriter); this.codecs = Preconditions.checkNotNull(codecFactory); this.tracker = SchemaTracker.create(codecFactory.getSchemaContext(), path); - this.context = new JSONStreamWriterRootContext(initialNs); + this.context = Preconditions.checkNotNull(rootContext); } /** - * Create a new stream writer, which writes to the specified {@link Writer}. + * Create a new stream writer, which writes to the specified output stream. * - * @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(JSONCodecFactory.create(schemaContext), SchemaPath.ROOT, writer, null, 0); - } - - /** - * Create a new stream writer, which writes to the specified {@link Writer}. + * The codec factory can be reused between multiple writers. * - * @param schemaContext Schema context - * @param path Root schemapath - * @param writer Output writer - * @return A stream writer instance - */ - public static NormalizedNodeStreamWriter create(final SchemaContext schemaContext, final SchemaPath path, final Writer writer) { - return new JSONNormalizedNodeStreamWriter(JSONCodecFactory.create(schemaContext), path, writer, null, 0); - } - - /** - * Create a new stream writer, which writes to the specified {@link Writer}. + * Returned writer is exclusive user of JsonWriter, which means it will start + * top-level JSON element and ends it. + * + * This instance of writer can be used only to emit one top level element, + * otherwise it will produce incorrect JSON. * - * @param schemaContext Schema context - * @param path Root schemapath - * @param writer Output writer + * @param codecFactory JSON codec factory + * @param path Schema Path * @param initialNs Initial namespace + * @param jsonWriter JsonWriter * @return A stream writer instance */ - public static NormalizedNodeStreamWriter create(final SchemaContext schemaContext, final SchemaPath path, - final URI initialNs, final Writer writer) { - return new JSONNormalizedNodeStreamWriter(JSONCodecFactory.create(schemaContext), path, writer, initialNs, 0); + public static NormalizedNodeStreamWriter createExclusiveWriter(final JSONCodecFactory codecFactory, final SchemaPath path, final URI initialNs, final JsonWriter jsonWriter) { + return new JSONNormalizedNodeStreamWriter(codecFactory, path, jsonWriter, new JSONStreamWriterExclusiveRootContext(initialNs)); } /** * 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(JSONCodecFactory.create(schemaContext), SchemaPath.ROOT, writer, null, indentSize); - } - - /** - * Create a new stream writer, which writes to the specified output stream. The codec factory - * can be reused between multiple writers. + * The codec factory can be reused between multiple writers. + * + * Returned writer can be used emit multiple top level element, + * but does not start / close parent JSON object, which must be done + * by user providing {@code jsonWriter} instance in order for + * JSON to be valid. * - * @param codecFactor JSON codec factory - * @param writer Output writer - * @param indentSize indentation size + * @param codecFactory JSON codec factory + * @param path Schema Path + * @param initialNs Initial namespace + * @param jsonWriter JsonWriter * @return A stream writer instance */ - public static NormalizedNodeStreamWriter create(final JSONCodecFactory codecFactory, final Writer writer, final int indentSize) { - return new JSONNormalizedNodeStreamWriter(codecFactory, SchemaPath.ROOT, writer, null, indentSize); + public static NormalizedNodeStreamWriter createNestedWriter(final JSONCodecFactory codecFactory, final SchemaPath path, final URI initialNs, final JsonWriter jsonWriter) { + return new JSONNormalizedNodeStreamWriter(codecFactory, path, jsonWriter, new JSONStreamWriterSharedRootContext(initialNs)); } @Override public void leafNode(final NodeIdentifier name, final Object value) throws IOException { final LeafSchemaNode schema = tracker.leafNode(name); - final JSONCodec codec = codecs.codecFor(schema.getType()); - - context.emittingChild(codecs.getSchemaContext(), writer, indent); + final JSONCodec codec = codecs.codecFor(schema); + context.emittingChild(codecs.getSchemaContext(), writer); context.writeChildJsonIdentifier(codecs.getSchemaContext(), writer, name.getNodeType()); - writeValue(codec.serialize(value), codec.needQuotes()); + writeValue(value, codec); } @Override @@ -148,10 +106,9 @@ public class JSONNormalizedNodeStreamWriter implements NormalizedNodeStreamWrite @Override public void leafSetEntryNode(final Object value) throws IOException { final LeafListSchemaNode schema = tracker.leafSetEntryNode(); - final JSONCodec codec = codecs.codecFor(schema.getType()); - - context.emittingChild(codecs.getSchemaContext(), writer, indent); - writeValue(codec.serialize(value), codec.needQuotes()); + final JSONCodec codec = codecs.codecFor(schema); + context.emittingChild(codecs.getSchemaContext(), writer); + writeValue(value, codec); } /* @@ -161,8 +118,11 @@ public class JSONNormalizedNodeStreamWriter implements NormalizedNodeStreamWrite @SuppressWarnings("unused") @Override public void startContainerNode(final NodeIdentifier name, final int childSizeHint) throws IOException { - final ContainerSchemaNode schema = tracker.startContainerNode(name); - context = new JSONStreamWriterNamedObjectContext(context, name, DEFAULT_EMIT_EMPTY_CONTAINERS || schema.isPresenceContainer()); + final SchemaNode schema = tracker.startContainerNode(name); + + // FIXME this code ignores presence for containers + // but datastore does as well and it needs be fixed first (2399) + context = new JSONStreamWriterNamedObjectContext(context, name, DEFAULT_EMIT_EMPTY_CONTAINERS); } @Override @@ -214,44 +174,33 @@ public class JSONNormalizedNodeStreamWriter implements NormalizedNodeStreamWrite final AnyXmlSchemaNode schema = tracker.anyxmlNode(name); // FIXME: should have a codec based on this :) - context.emittingChild(codecs.getSchemaContext(), writer, indent); + context.emittingChild(codecs.getSchemaContext(), writer); context.writeChildJsonIdentifier(codecs.getSchemaContext(), writer, name.getNodeType()); - writeValue(String.valueOf(value), true); + // FIXME this kind of serialization is incorrect since the value for AnyXml is now a DOMSource + writer.value(String.valueOf(value)); } @Override - public void endNode() throws IOException { - tracker.endNode(); - context = context.endNode(codecs.getSchemaContext(), writer, indent); + public void startYangModeledAnyXmlNode(final NodeIdentifier name, final int childSizeHint) throws IOException { + tracker.startYangModeledAnyXmlNode(name); + context = new JSONStreamWriterNamedObjectContext(context, name, true); } - private void writeValue(final String str, final boolean needQuotes) throws IOException { - if (needQuotes) { - writer.append('"'); - - final int needEscape = JSON_ILLEGAL_STRING_CHARACTERS.countIn(str); - if (needEscape != 0) { - final char[] escaped = new char[str.length() + needEscape]; - int offset = 0; - - for (int i = 0; i < str.length(); i++) { - final char c = str.charAt(i); - if (JSON_ILLEGAL_STRING_CHARACTERS.matches(c)) { - escaped[offset++] = '\\'; - } - escaped[offset++] = c; - } - writer.write(escaped); - } else { - writer.append(str); - } + @Override + public void endNode() throws IOException { + tracker.endNode(); + context = context.endNode(codecs.getSchemaContext(), writer); - writer.append('"'); - } else { - writer.append(str); + if(context instanceof JSONStreamWriterRootContext) { + context.emitEnd(writer); } } + private void writeValue(final Object value, final JSONCodec codec) + throws IOException { + codec.serializeToWriter(writer,value); + } + @Override public void flush() throws IOException { writer.flush(); @@ -259,8 +208,7 @@ public class JSONNormalizedNodeStreamWriter implements NormalizedNodeStreamWrite @Override public void close() throws IOException { - writer.flush(); + flush(); writer.close(); } - }