/* * Copyright (c) 2014 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 static com.google.common.base.Preconditions.checkState; import static java.util.Objects.requireNonNull; import com.google.common.collect.ClassToInstanceMap; import com.google.common.collect.ImmutableClassToInstanceMap; import com.google.gson.stream.JsonWriter; import java.io.IOException; import java.util.NoSuchElementException; import java.util.regex.Pattern; import javax.xml.transform.dom.DOMSource; import org.checkerframework.checker.regex.qual.Regex; import org.opendaylight.yangtools.rfc8528.data.api.MountPointContext; import org.opendaylight.yangtools.rfc8528.data.api.MountPointIdentifier; import org.opendaylight.yangtools.rfc8528.data.api.StreamWriterMountPointExtension; import org.opendaylight.yangtools.yang.common.XMLNamespace; 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.YangInstanceIdentifier.NodeWithValue; import org.opendaylight.yangtools.yang.data.api.schema.NormalizedAnydata; import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter; import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriterExtension; import org.opendaylight.yangtools.yang.data.util.NormalizedNodeStreamWriterStack; import org.opendaylight.yangtools.yang.model.api.AnydataSchemaNode; import org.opendaylight.yangtools.yang.model.api.AnyxmlSchemaNode; import org.opendaylight.yangtools.yang.model.api.ContainerSchemaNode; import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; import org.opendaylight.yangtools.yang.model.api.EffectiveStatementInference; import org.opendaylight.yangtools.yang.model.api.SchemaNode; import org.opendaylight.yangtools.yang.model.api.SchemaPath; import org.opendaylight.yangtools.yang.model.api.TypedDataSchemaNode; import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Absolute; import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.w3c.dom.Text; /** * This implementation will create JSON output as output stream. * *
* Values of leaf and leaf-list are NOT translated according to codecs. */ public abstract class JSONNormalizedNodeStreamWriter implements NormalizedNodeStreamWriter, StreamWriterMountPointExtension { private static final class Exclusive extends JSONNormalizedNodeStreamWriter { Exclusive(final JSONCodecFactory codecFactory, final NormalizedNodeStreamWriterStack tracker, final JsonWriter writer, final JSONStreamWriterRootContext rootContext) { super(codecFactory, tracker, writer, rootContext); } @Override public void close() throws IOException { flush(); closeWriter(); } } private static final class Nested extends JSONNormalizedNodeStreamWriter { Nested(final JSONCodecFactory codecFactory, final NormalizedNodeStreamWriterStack tracker, final JsonWriter writer, final JSONStreamWriterRootContext rootContext) { super(codecFactory, tracker, writer, rootContext); } @Override public void close() throws IOException { flush(); // The caller "owns" the writer, let them close it } } /** * 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; @Regex private static final String NUMBER_STRING = "-?\\d+(\\.\\d+)?"; private static final Pattern NUMBER_PATTERN = Pattern.compile(NUMBER_STRING); @Regex private static final String NOT_DECIMAL_NUMBER_STRING = "-?\\d+"; private static final Pattern NOT_DECIMAL_NUMBER_PATTERN = Pattern.compile(NOT_DECIMAL_NUMBER_STRING); private final NormalizedNodeStreamWriterStack tracker; private final JSONCodecFactory codecs; private final JsonWriter writer; private JSONStreamWriterContext context; JSONNormalizedNodeStreamWriter(final JSONCodecFactory codecFactory, final NormalizedNodeStreamWriterStack 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 XMLNamespace initialNs, final JsonWriter jsonWriter) { return new Exclusive(codecFactory, NormalizedNodeStreamWriterStack.of(codecFactory.getEffectiveModelContext(), path), jsonWriter, new JSONStreamWriterExclusiveRootContext(initialNs)); } /** * 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 inference * @param initialNs Initial namespace * @param jsonWriter JsonWriter * @return A stream writer instance */ public static NormalizedNodeStreamWriter createExclusiveWriter(final JSONCodecFactory codecFactory, final EffectiveStatementInference rootNode, final XMLNamespace initialNs, final JsonWriter jsonWriter) { return new Exclusive(codecFactory, NormalizedNodeStreamWriterStack.of(rootNode), jsonWriter, new JSONStreamWriterExclusiveRootContext(initialNs)); } /** * 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 Absolute path, final XMLNamespace initialNs, final JsonWriter jsonWriter) { return new Exclusive(codecFactory, NormalizedNodeStreamWriterStack.of(codecFactory.getEffectiveModelContext(), path), jsonWriter, new JSONStreamWriterExclusiveRootContext(initialNs)); } /** * Create a new stream writer, which writes to the specified output stream. * *
* 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 path Schema Path * @param initialNs Initial namespace * @param jsonWriter JsonWriter * @return A stream writer instance */ public static NormalizedNodeStreamWriter createNestedWriter(final JSONCodecFactory codecFactory, final SchemaPath path, final XMLNamespace initialNs, final JsonWriter jsonWriter) { return new Nested(codecFactory, NormalizedNodeStreamWriterStack.of(codecFactory.getEffectiveModelContext(), path), jsonWriter, new JSONStreamWriterSharedRootContext(initialNs)); } /** * Create a new stream writer, which writes to the specified output stream. * *
* 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 path Schema Path * @param initialNs Initial namespace * @param jsonWriter JsonWriter * @return A stream writer instance */ public static NormalizedNodeStreamWriter createNestedWriter(final JSONCodecFactory codecFactory, final Absolute path, final XMLNamespace initialNs, final JsonWriter jsonWriter) { return new Nested(codecFactory, NormalizedNodeStreamWriterStack.of(codecFactory.getEffectiveModelContext(), path), jsonWriter, new JSONStreamWriterSharedRootContext(initialNs)); } /** * Create a new stream writer, which writes to the specified output stream. * *
* 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 inference
* @param initialNs Initial namespace
* @param jsonWriter JsonWriter
* @return A stream writer instance
*/
public static NormalizedNodeStreamWriter createNestedWriter(final JSONCodecFactory codecFactory,
final EffectiveStatementInference rootNode, final XMLNamespace initialNs, final JsonWriter jsonWriter) {
return new Nested(codecFactory, NormalizedNodeStreamWriterStack.of(rootNode), jsonWriter,
new JSONStreamWriterSharedRootContext(initialNs));
}
@Override
public ClassToInstanceMap