* 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 abstract class JSONNormalizedNodeStreamWriter implements NormalizedNodeStreamWriter {
+ private static final class Exclusive extends JSONNormalizedNodeStreamWriter {
+ Exclusive(final JSONCodecFactory codecFactory, final SchemaTracker tracker, final JsonWriter writer,
+ final JSONStreamWriterRootContext rootContext) {
+ super(codecFactory, tracker, writer, rootContext);
}
- public NodeType getType() {
- return type;
+ @Override
+ public void close() throws IOException {
+ flush();
+ closeWriter();
}
+ }
- public URI getNamespace() {
- return uri;
+ private static final class Nested extends JSONNormalizedNodeStreamWriter {
+ Nested(final JSONCodecFactory codecFactory, final SchemaTracker tracker, final JsonWriter writer,
+ final JSONStreamWriterRootContext rootContext) {
+ super(codecFactory, tracker, writer, rootContext);
}
- public boolean hasAtLeastOneChild() {
- return hasAtLeastOneChild;
+ @Override
+ public void close() throws IOException {
+ flush();
+ // The caller "owns" the writer, let them close it
}
}
- private final Deque stack = new ArrayDeque<>();
- private final SchemaContext schemaContext;
- private final CodecFactory codecs;
- private final SchemaTracker tracker;
- private final Writer writer;
- private final String indent;
+ /**
+ * 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;
- private URI currentNamespace = null;
- private int currentDepth = 0;
+ @RegEx
+ private static final String NUMBER_STRING = "-?\\d+(\\.\\d+)?";
+ private static final Pattern NUMBER_PATTERN = Pattern.compile(NUMBER_STRING);
- private JSONNormalizedNodeStreamWriter(final SchemaContext schemaContext,
- final Writer writer, final int indentSize) {
- this.schemaContext = Preconditions.checkNotNull(schemaContext);
- this.writer = Preconditions.checkNotNull(writer);
+ @RegEx
+ private static final String NOT_DECIMAL_NUMBER_STRING = "-?\\d+";
+ private static final Pattern NOT_DECIMAL_NUMBER_PATTERN = Pattern.compile(NOT_DECIMAL_NUMBER_STRING);
- Preconditions.checkArgument(indentSize >= 0, "Indent size must be non-negative");
- if (indentSize != 0) {
- indent = Strings.repeat(" ", indentSize);
- } else {
- indent = null;
- }
+ private final SchemaTracker tracker;
+ private final JSONCodecFactory codecs;
+ private final JsonWriter writer;
+ private JSONStreamWriterContext context;
+
+ JSONNormalizedNodeStreamWriter(final JSONCodecFactory codecFactory, final SchemaTracker tracker,
+ final JsonWriter writer, final JSONStreamWriterRootContext rootContext) {
+ this.writer = requireNonNull(writer);
+ this.codecs = requireNonNull(codecFactory);
+ this.tracker = requireNonNull(tracker);
+ this.context = requireNonNull(rootContext);
+ }
+
+ /**
+ * Create a new stream writer, which writes to the specified output stream.
+ *
+ *
+ * The codec factory can be reused between multiple writers.
+ *
+ *
+ * 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. Closing this instance will close
+ * the writer too.
+ *
+ * @param codecFactory JSON codec factory
+ * @param path Schema Path
+ * @param initialNs Initial namespace
+ * @param jsonWriter JsonWriter
+ * @return A stream writer instance
+ */
+ public static NormalizedNodeStreamWriter createExclusiveWriter(final JSONCodecFactory codecFactory,
+ final SchemaPath path, final URI initialNs, final JsonWriter jsonWriter) {
+ return new Exclusive(codecFactory, SchemaTracker.create(codecFactory.getSchemaContext(), path), jsonWriter,
+ new JSONStreamWriterExclusiveRootContext(initialNs));
+ }
- this.codecs = CodecFactory.create(schemaContext);
- this.tracker = SchemaTracker.create(schemaContext);
+ /**
+ * Create a new stream writer, which writes to the specified output stream.
+ *
+ *
+ * The codec factory can be reused between multiple writers.
+ *
+ *
+ * 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. Closing this instance will close
+ * the writer too.
+ *
+ * @param codecFactory JSON codec factory
+ * @param rootNode Root node
+ * @param initialNs Initial namespace
+ * @param jsonWriter JsonWriter
+ * @return A stream writer instance
+ */
+ public static NormalizedNodeStreamWriter createExclusiveWriter(final JSONCodecFactory codecFactory,
+ final DataNodeContainer rootNode, final URI initialNs, final JsonWriter jsonWriter) {
+ return new Exclusive(codecFactory, SchemaTracker.create(rootNode), jsonWriter,
+ new JSONStreamWriterExclusiveRootContext(initialNs));
}
/**
- * Create a new stream writer, which writes to the specified {@link Writer}.
+ * Create a new stream writer, which writes to the specified output stream.
+ *
+ *
+ * The codec factory can be reused between multiple writers.
*
- * @param schemaContext Schema context
- * @param writer Output writer
+ *
+ * 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. Closing this instance will not
+ * close the wrapped writer; the caller must take care of that.
+ *
+ * @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 Writer writer) {
- return new JSONNormalizedNodeStreamWriter(schemaContext, writer, 0);
+ public static NormalizedNodeStreamWriter createNestedWriter(final JSONCodecFactory codecFactory,
+ final SchemaPath path, final URI initialNs, final JsonWriter jsonWriter) {
+ return new Nested(codecFactory, SchemaTracker.create(codecFactory.getSchemaContext(), path), jsonWriter,
+ new JSONStreamWriterSharedRootContext(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
+ *
+ * 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. Closing this instance will not
+ * close the wrapped writer; the caller must take care of that.
+ *
+ * @param codecFactory JSON codec factory
+ * @param rootNode Root node
+ * @param initialNs Initial namespace
+ * @param jsonWriter JsonWriter
* @return A stream writer instance
*/
- public static NormalizedNodeStreamWriter create(final SchemaContext schemaContext, final Writer writer, final int indentSize) {
- return new JSONNormalizedNodeStreamWriter(schemaContext, writer, indentSize);
+ public static NormalizedNodeStreamWriter createNestedWriter(final JSONCodecFactory codecFactory,
+ final DataNodeContainer rootNode, final URI initialNs, final JsonWriter jsonWriter) {
+ return new Nested(codecFactory, SchemaTracker.create(rootNode), jsonWriter,
+ new JSONStreamWriterSharedRootContext(initialNs));
}
@Override
- public void leafNode(final NodeIdentifier name, final Object value) throws IOException {
+ public final void leafNode(final NodeIdentifier name, final Object value) throws IOException {
final LeafSchemaNode schema = tracker.leafNode(name);
- final Codec