Add YinXMLEventReaderFactory 92/67892/13
authorRobert Varga <robert.varga@pantheon.tech>
Sat, 3 Feb 2018 15:29:45 +0000 (16:29 +0100)
committerRobert Varga <robert.varga@pantheon.tech>
Tue, 6 Mar 2018 13:46:24 +0000 (14:46 +0100)
This is a proper way of exporting YIN models from parser output,
being more correct and much simpler than YinExportUtils.
YinExportUtils.writeModuleToOutputStream() methods are deprecated
in favor of writeModuleAsYinText() and writeSubmoduleAsYinText().

JIRA: YANGTOOLS-858
Change-Id: I9fda3ef3ccf2f3aa9e18d32da85e225d835151f9
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
19 files changed:
yang/yang-model-export/src/main/java/org/opendaylight/yangtools/yang/model/export/ExportUtils.java [new file with mode: 0644]
yang/yang-model-export/src/main/java/org/opendaylight/yangtools/yang/model/export/ExtensionStatement.java
yang/yang-model-export/src/main/java/org/opendaylight/yangtools/yang/model/export/ModuleNamespaceContext.java [new file with mode: 0644]
yang/yang-model-export/src/main/java/org/opendaylight/yangtools/yang/model/export/SchemaContextEmitter.java
yang/yang-model-export/src/main/java/org/opendaylight/yangtools/yang/model/export/SchemaToStatementWriterAdaptor.java
yang/yang-model-export/src/main/java/org/opendaylight/yangtools/yang/model/export/SingleModuleYinStatementWriter.java
yang/yang-model-export/src/main/java/org/opendaylight/yangtools/yang/model/export/StatementTextWriter.java
yang/yang-model-export/src/main/java/org/opendaylight/yangtools/yang/model/export/YangModuleWriter.java
yang/yang-model-export/src/main/java/org/opendaylight/yangtools/yang/model/export/YangTextSnippetIterator.java
yang/yang-model-export/src/main/java/org/opendaylight/yangtools/yang/model/export/YinExportUtils.java
yang/yang-model-export/src/main/java/org/opendaylight/yangtools/yang/model/export/YinXMLEventReader.java [new file with mode: 0644]
yang/yang-model-export/src/main/java/org/opendaylight/yangtools/yang/model/export/YinXMLEventReaderFactory.java [new file with mode: 0644]
yang/yang-model-export/src/test/java/org/opendaylight/yangtools/yang/model/export/AbstractYinExportTest.java [new file with mode: 0644]
yang/yang-model-export/src/test/java/org/opendaylight/yangtools/yang/model/export/Bug2444Test.java
yang/yang-model-export/src/test/java/org/opendaylight/yangtools/yang/model/export/Bug4504Test.java
yang/yang-model-export/src/test/java/org/opendaylight/yangtools/yang/model/export/EffectiveSchemaContextEmitterTest.java
yang/yang-model-export/src/test/java/org/opendaylight/yangtools/yang/model/export/SchemaContextEmitterTest.java
yang/yang-model-export/src/test/resources/schema-context-emitter-test/foo@2016-08-05.yang [moved from yang/yang-model-export/src/test/resources/schema-context-emitter-test/foo.yang with 100% similarity]
yang/yang-model-export/src/test/resources/schema-context-emitter-test/foo@2016-08-05.yin [moved from yang/yang-model-export/src/test/resources/schema-context-emitter-test/foo.yin with 100% similarity]

diff --git a/yang/yang-model-export/src/main/java/org/opendaylight/yangtools/yang/model/export/ExportUtils.java b/yang/yang-model-export/src/main/java/org/opendaylight/yangtools/yang/model/export/ExportUtils.java
new file mode 100644 (file)
index 0000000..1baf800
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.model.export;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Verify.verify;
+
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.Revision;
+import org.opendaylight.yangtools.yang.common.YangConstants;
+
+/**
+ * Internal shared helpers.
+ * @author Robert Varga
+ *
+ */
+final class ExportUtils {
+    private ExportUtils() {
+        // Hidden on purpose
+    }
+
+    static Optional<String> statementPrefix(final Map<QNameModule, String> namespaces, final QName stmtName) {
+        final QNameModule namespace = stmtName.getModule();
+        if (YangConstants.RFC6020_YIN_MODULE.equals(namespace)) {
+            return Optional.empty();
+        }
+
+        // Non-default namespace, a prefix is needed
+        @Nullable String prefix = namespaces.get(namespace);
+        if (prefix == null && !namespace.getRevision().isPresent()) {
+            // FIXME: this is an artifact of commonly-bound statements in parser, which means a statement's name
+            //        does not have a Revision. We'll need to find a solution to this which is acceptable. There
+            //        are multiple ways of fixing this:
+            //        - perhaps EffectiveModuleStatement should be giving us a statement-to-EffectiveModule map?
+            //        - or DeclaredStatement should provide the prefix?
+            //        The second one seems cleaner, as that means we would not have perform any lookup at all...
+            Entry<QNameModule, @NonNull String> match = null;
+            for (Entry<QNameModule, @NonNull String> entry : namespaces.entrySet()) {
+                final QNameModule ns = entry.getKey();
+                if (namespace.equals(ns.withoutRevision()) && (match == null
+                        || Revision.compare(match.getKey().getRevision(), ns.getRevision()) < 0)) {
+                    match = entry;
+                }
+            }
+
+            if (match != null) {
+                prefix = match.getValue();
+            }
+        }
+
+        checkArgument(prefix != null, "Failed to find prefix for statement %s", stmtName);
+        verify(!prefix.isEmpty(), "Empty prefix for statement %s", stmtName);
+        return Optional.of(prefix);
+    }
+}
index f02b8231b614f60fed1459b89547db2f2c242a0a..1feb02e2c07f2515ac3366b5aea2e7f10dfd6717 100644 (file)
@@ -17,11 +17,12 @@ import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
 import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
 
+@Deprecated
 final class ExtensionStatement implements StatementDefinition {
 
-    private QName argumentName;
-    private QName statementName;
-    private boolean yinElement;
+    private final QName argumentName;
+    private final QName statementName;
+    private final boolean yinElement;
 
     private ExtensionStatement(final ExtensionDefinition def) {
         statementName = def.getQName();
diff --git a/yang/yang-model-export/src/main/java/org/opendaylight/yangtools/yang/model/export/ModuleNamespaceContext.java b/yang/yang-model-export/src/main/java/org/opendaylight/yangtools/yang/model/export/ModuleNamespaceContext.java
new file mode 100644 (file)
index 0000000..c74562e
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.model.export;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.collect.ImmutableListMultimap;
+import com.google.common.collect.ImmutableListMultimap.Builder;
+import com.google.common.collect.Iterators;
+import com.google.common.collect.ListMultimap;
+import com.google.common.collect.Maps;
+import java.util.AbstractMap.SimpleImmutableEntry;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+import javax.xml.XMLConstants;
+import javax.xml.namespace.NamespaceContext;
+import org.eclipse.jdt.annotation.NonNull;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
+import org.opendaylight.yangtools.yang.common.YangConstants;
+import org.opendaylight.yangtools.yang.model.api.stmt.ModuleEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ModuleEffectiveStatement.PrefixToEffectiveModuleNamespace;
+import org.opendaylight.yangtools.yang.model.api.stmt.ModuleEffectiveStatement.QNameModuleToPrefixNamespace;
+
+final class ModuleNamespaceContext implements NamespaceContext {
+    private static final Entry<String, String> YIN_PREFIX_AND_NAMESPACE =
+            new SimpleImmutableEntry<>(XMLConstants.DEFAULT_NS_PREFIX, YangConstants.RFC6020_YIN_NAMESPACE_STRING);
+
+    private final ListMultimap<@NonNull String, @NonNull String> namespaceToPrefix;
+    private final Map<String, @NonNull ModuleEffectiveStatement> prefixToModule;
+    private final Map<QNameModule, @NonNull String> moduleToPrefix;
+
+    ModuleNamespaceContext(final ModuleEffectiveStatement module) {
+        this.prefixToModule = requireNonNull(module.getAll(PrefixToEffectiveModuleNamespace.class));
+        this.moduleToPrefix = requireNonNull(module.getAll(QNameModuleToPrefixNamespace.class));
+
+        final Builder<String, String> namespaces = ImmutableListMultimap.builder();
+        for (Entry<QNameModule, @NonNull String> e : moduleToPrefix.entrySet()) {
+            namespaces.put(e.getKey().getNamespace().toString(), e.getValue());
+        }
+        namespaceToPrefix = namespaces.build();
+    }
+
+    @Override
+    public String getNamespaceURI(final String prefix) {
+        checkArgument(prefix != null);
+
+        switch (prefix) {
+            case XMLConstants.DEFAULT_NS_PREFIX:
+                return YangConstants.RFC6020_YIN_NAMESPACE_STRING;
+            case XMLConstants.XML_NS_PREFIX:
+                return XMLConstants.XML_NS_URI;
+            case XMLConstants.XMLNS_ATTRIBUTE:
+                return XMLConstants.XMLNS_ATTRIBUTE_NS_URI;
+            default:
+                final ModuleEffectiveStatement module = prefixToModule.get(prefix);
+                return module != null ? module.localQNameModule().getNamespace().toString()
+                        : XMLConstants.NULL_NS_URI;
+        }
+    }
+
+    @Override
+    public String getPrefix(final String namespaceURI) {
+        checkArgument(namespaceURI != null);
+
+        switch (namespaceURI) {
+            case YangConstants.RFC6020_YIN_NAMESPACE_STRING:
+                return XMLConstants.DEFAULT_NS_PREFIX;
+            case XMLConstants.XML_NS_URI:
+                return XMLConstants.XML_NS_PREFIX;
+            case XMLConstants.XMLNS_ATTRIBUTE_NS_URI:
+                return XMLConstants.XMLNS_ATTRIBUTE;
+            default:
+                final List<@NonNull String> prefixes = namespaceToPrefix.get(namespaceURI);
+                return prefixes.isEmpty() ? null : prefixes.get(0);
+        }
+    }
+
+    @Override
+    public Iterator<String> getPrefixes(final String namespaceURI) {
+        checkArgument(namespaceURI != null);
+
+        switch (namespaceURI) {
+            case YangConstants.RFC6020_YIN_NAMESPACE_STRING:
+                return Iterators.singletonIterator(XMLConstants.DEFAULT_NS_PREFIX);
+            case XMLConstants.XML_NS_URI:
+                return Iterators.singletonIterator(XMLConstants.XML_NS_PREFIX);
+            case XMLConstants.XMLNS_ATTRIBUTE_NS_URI:
+                return Iterators.singletonIterator(XMLConstants.XMLNS_ATTRIBUTE);
+            default:
+                return namespaceToPrefix.get(namespaceURI).iterator();
+        }
+    }
+
+    Entry<String, String> prefixAndNamespaceFor(final QNameModule module) {
+        if (YangConstants.RFC6020_YIN_MODULE.equals(module)) {
+            return YIN_PREFIX_AND_NAMESPACE;
+        }
+
+        final String prefix = moduleToPrefix.get(module);
+        checkArgument(prefix != null, "Module %s does not map to a prefix", module);
+        return new SimpleImmutableEntry<>(prefix, module.getNamespace().toString());
+    }
+
+    Entry<String, String> prefixAndNamespaceForStatement(final QName stmtName) {
+        final Optional<String> prefix = ExportUtils.statementPrefix(moduleToPrefix, stmtName);
+        if (!prefix.isPresent()) {
+            return YIN_PREFIX_AND_NAMESPACE;
+        }
+
+        return new SimpleImmutableEntry<>(prefix.get(), stmtName.getNamespace().toString());
+    }
+
+    Map<String, String> prefixesAndNamespaces() {
+        return Maps.transformValues(prefixToModule, module -> module.localQNameModule().getNamespace().toString());
+    }
+}
index cdb594edbc95c17d9bc65db92b44a93e299d4a79..875711c1fd760a2b0109111142ba39e8bb6f6dd1 100644 (file)
@@ -165,6 +165,7 @@ import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition;
 import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition;
 import org.opendaylight.yangtools.yang.model.util.SchemaNodeUtils;
 
+@Deprecated
 @Beta
 @NotThreadSafe
 abstract class SchemaContextEmitter {
index f86f9e99a91f946990a66511b0e7303d8bb6170a..7e930b0527aaf4a38daf41daa3b968b77d781050 100644 (file)
@@ -27,6 +27,7 @@ import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier.Relative;
 import org.opendaylight.yangtools.yang.model.api.type.ModifierKind;
 
+@Deprecated
 @Beta
 @NotThreadSafe
 final class SchemaToStatementWriterAdaptor implements YangModuleWriter {
index 1746a0e53589f25cfd1079d5b96a21e2caf71ce7..59a4f784aba65063605da37d9c63cb6a83cf27b0 100644 (file)
@@ -23,10 +23,11 @@ import javax.xml.stream.XMLStreamWriter;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.YangConstants;
 import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
-import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
 import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
 
+@Deprecated
 @Beta
 @NotThreadSafe
 class SingleModuleYinStatementWriter implements StatementTextWriter {
index 3d74e37e53425ee27ed28ae55685be6b7295621a..8bcf0e78a61fddb029000e5f919b93b5393e76bc 100644 (file)
@@ -13,6 +13,7 @@ import org.opendaylight.yangtools.yang.model.api.RevisionAwareXPath;
 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
 import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
 
+@Deprecated
 interface StatementTextWriter {
 
 
index 0b4baccce1bd241094ffcdef641fe664183b058b..dab4bff1677b5b2d634cbb16e40979c9113ac5dd 100644 (file)
@@ -19,6 +19,7 @@ import org.opendaylight.yangtools.yang.model.api.UniqueConstraint;
 import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
 import org.opendaylight.yangtools.yang.model.api.type.ModifierKind;
 
+@Deprecated
 interface YangModuleWriter {
 
     void endNode();
index 8293214b1079daa958b997125313fe625a5fa991..4e18d885e1bacf85d301ee86f2303e101dfd84c3 100644 (file)
@@ -7,8 +7,6 @@
  */
 package org.opendaylight.yangtools.yang.model.export;
 
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Verify.verify;
 import static java.util.Objects.requireNonNull;
 import static org.eclipse.jdt.annotation.DefaultLocation.PARAMETER;
 import static org.eclipse.jdt.annotation.DefaultLocation.RETURN_TYPE;
@@ -24,7 +22,7 @@ import java.util.Collection;
 import java.util.Deque;
 import java.util.Iterator;
 import java.util.Map;
-import java.util.Map.Entry;
+import java.util.Optional;
 import java.util.Queue;
 import java.util.Set;
 import org.eclipse.jdt.annotation.NonNull;
@@ -32,8 +30,6 @@ import org.eclipse.jdt.annotation.NonNullByDefault;
 import org.eclipse.jdt.annotation.Nullable;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.QNameModule;
-import org.opendaylight.yangtools.yang.common.Revision;
-import org.opendaylight.yangtools.yang.common.YangConstants;
 import org.opendaylight.yangtools.yang.model.api.YangStmtMapping;
 import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
 import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
@@ -200,34 +196,9 @@ final class YangTextSnippetIterator extends AbstractIterator<@NonNull String> {
 
         // Add statement prefixed with namespace if needed
         final QName stmtName = def.getStatementName();
-        final QNameModule namespace = stmtName.getModule();
-        if (!YangConstants.RFC6020_YIN_MODULE.equals(namespace)) {
-            // Non-default namespace, a prefix is needed
-            @Nullable String prefix = namespaces.get(namespace);
-            if (prefix == null && !namespace.getRevision().isPresent()) {
-                // FIXME: this is an artifact of commonly-bound statements in parser, which means a statement's name
-                //        does not have a Revision. We'll need to find a solution to this which is acceptable. There
-                //        are multiple ways of fixing this:
-                //        - perhaps EffectiveModuleStatement should be giving us a statement-to-EffectiveModule map?
-                //        - or DeclaredStatement should provide the prefix?
-                //        The second one seems cleaner, as that means we would not have perform any lookup at all...
-                Entry<QNameModule, @NonNull String> match = null;
-                for (Entry<QNameModule, @NonNull String> entry : namespaces.entrySet()) {
-                    final QNameModule ns = entry.getKey();
-                    if (namespace.equals(ns.withoutRevision()) && (match == null
-                            || Revision.compare(match.getKey().getRevision(), ns.getRevision()) < 0)) {
-                            match = entry;
-                    }
-                }
-
-                if (match != null) {
-                    prefix = match.getValue();
-                }
-            }
-
-            checkArgument(prefix != null, "Failed to find prefix for statement %s", stmtName);
-            verify(!prefix.isEmpty(), "Empty prefix for statement %s", stmtName);
-            strings.add(prefix);
+        final Optional<String> prefix = ExportUtils.statementPrefix(namespaces, stmtName);
+        if (prefix.isPresent()) {
+            strings.add(prefix.get());
             strings.add(":");
         }
         strings.add(stmtName.getLocalName());
index 510fdab8337349a059362463e92c2d8189f988e6..9eaffe6dfc5a4f0f6b182abfafa4fdb10c11a4f5 100644 (file)
@@ -7,36 +7,47 @@
  */
 package org.opendaylight.yangtools.yang.model.export;
 
-import com.google.common.base.Preconditions;
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
 import com.google.common.collect.BiMap;
 import com.google.common.collect.HashBiMap;
 import java.io.OutputStream;
 import java.net.URI;
 import java.util.Map;
 import java.util.Optional;
+import javax.xml.stream.XMLEventReader;
 import javax.xml.stream.XMLOutputFactory;
 import javax.xml.stream.XMLStreamException;
 import javax.xml.stream.XMLStreamWriter;
+import javax.xml.transform.OutputKeys;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.stax.StAXSource;
+import javax.xml.transform.stream.StreamResult;
 import org.opendaylight.yangtools.yang.common.Revision;
 import org.opendaylight.yangtools.yang.common.YangConstants;
 import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.ModuleImport;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.model.api.stmt.ModuleEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SubmoduleEffectiveStatement;
 
 public final class YinExportUtils {
+    private static final TransformerFactory TRANSFORMER_FACTORY = TransformerFactory.newInstance();
+    private static final XMLOutputFactory OUTPUT_FACTORY = XMLOutputFactory.newFactory();
 
     private YinExportUtils() {
         throw new UnsupportedOperationException("Utility class");
     }
 
     /**
-     *
      * Returns well-formed file name of YIN file as defined in RFC6020.
      *
-     * @param name
-     *            Module or submodule name
-     * @param revision
-     *            Revision of module or submodule
+     * @param name Module or submodule name
+     * @param revision Revision of module or submodule
      * @return well-formed file name of YIN file as defined in RFC6020.
      */
     public static String wellFormedYinName(final String name, final Optional<Revision> revision) {
@@ -44,35 +55,73 @@ public final class YinExportUtils {
     }
 
     /**
-     *
      * Returns well-formed file name of YIN file as defined in RFC6020.
      *
-     * @param name
-     *            name Module or submodule name
-     * @param revision
-     *            Revision of module or submodule
+     * @param name name Module or submodule name
+     * @param revision Revision of module or submodule
      * @return well-formed file name of YIN file as defined in RFC6020.
      */
     public static String wellFormedYinName(final String name, final String revision) {
         if (revision == null) {
             return name + YangConstants.RFC6020_YIN_FILE_EXTENSION;
         }
-        return Preconditions.checkNotNull(name) + '@' + revision +  YangConstants.RFC6020_YIN_FILE_EXTENSION;
+        return requireNonNull(name) + '@' + revision +  YangConstants.RFC6020_YIN_FILE_EXTENSION;
+    }
+
+    /**
+     * Write a module as a YIN text into specified {@link OutputStream}. Supplied module must have the
+     * {@link ModuleEffectiveStatement} trait.
+     *
+     * @param module Module to be exported
+     * @throws IllegalArgumentException if the module is not an ModuleEffectiveStatement or if it declared
+     *                                  representation is not available.
+     * @throws NullPointerException if any of of the parameters is null
+     * @throws XMLStreamException if an input-output error occurs
+     */
+    @Beta
+    public static void writeModuleAsYinText(final Module module, final OutputStream output) throws XMLStreamException {
+        requireNonNull(module);
+        checkArgument(module instanceof ModuleEffectiveStatement, "Module %s is not a ModuleEffectiveStatement",
+            module);
+        final ModuleEffectiveStatement effective = (ModuleEffectiveStatement) module;
+        writeReaderToOutput(YinXMLEventReaderFactory.defaultInstance().createXMLEventReader(effective), output);
+    }
+
+    /**
+     * Write a submodule as a YIN text into specified {@link OutputStream}. Supplied submodule must have the
+     * {@link SubmoduleEffectiveStatement} trait.
+     *
+     * @param parentModule Parent module
+     * @param submodule Submodule to be exported
+     * @throws IllegalArgumentException if the parent module is not a ModuleEffectiveStatement, if the submodule is not
+     *                                  a SubmoduleEffectiveStatement or if its declared representation is not available
+     * @throws NullPointerException if any of of the parameters is null
+     * @throws XMLStreamException if an input-output error occurs
+     */
+    @Beta
+    public static void writeSubmoduleAsYinText(final Module parentModule, final Module submodule,
+            final OutputStream output) throws XMLStreamException {
+        requireNonNull(parentModule);
+        checkArgument(parentModule instanceof ModuleEffectiveStatement, "Parent %s is not a ModuleEffectiveStatement",
+            parentModule);
+        requireNonNull(submodule);
+        checkArgument(submodule instanceof SubmoduleEffectiveStatement,
+            "Submodule %s is not a SubmoduleEffectiveStatement", submodule);
+        writeReaderToOutput(YinXMLEventReaderFactory.defaultInstance().createXMLEventReader(
+            (ModuleEffectiveStatement) parentModule, (SubmoduleEffectiveStatement)submodule), output);
     }
 
     /**
      * Writes YIN representation of supplied module to specified output stream.
      *
-     * @param ctx
-     *            Schema Context which contains module and extension definitions
-     *            to be used during export of model.
-     * @param module
-     *            Module to be exported.
-     * @param str
-     *            Output stream to which YIN representation of model will be
-     *            written.
-     * @throws XMLStreamException
+     * @param ctx Schema Context which contains module and extension definitions to be used during export of model.
+     * @param module Module to be exported.
+     * @param str Output stream to which YIN representation of model will be written.
+     * @throws XMLStreamException when a streaming problem occurs
+     * @deprecated Use {@link #writeModuleAsYinText(Module, OutputStream)}
+     *             or {@link #writeSubmoduleAsYinText(Module, Module, OutputStream)} instead.
      */
+    @Deprecated
     public static void writeModuleToOutputStream(final SchemaContext ctx, final Module module, final OutputStream str)
             throws XMLStreamException {
         writeModuleToOutputStream(ctx, module, str, false);
@@ -81,23 +130,18 @@ public final class YinExportUtils {
     /**
      * Writes YIN representation of supplied module to specified output stream.
      *
-     * @param ctx
-     *            Schema Context which contains module and extension definitions
-     *            to be used during export of model.
-     * @param module
-     *            Module to be exported.
-     * @param str
-     *            Output stream to which YIN representation of model will be
-     *            written.
-     * @param emitInstantiated
-     *            Option to emit also instantiated statements (e.g. statements
-     *            added by uses or augment)
-     * @throws XMLStreamException
+     * @param ctx Schema Context which contains module and extension definitions to be used during export of model.
+     * @param module Module to be exported.
+     * @param str Output stream to which YIN representation of model will be written.
+     * @param emitInstantiated Option to emit also instantiated statements (e.g. statements added by uses or augment)
+     * @throws XMLStreamException when a streaming problem occurs
+     * @deprecated Use {@link #writeModuleAsYinText(Module, OutputStream)}
+     *             or {@link #writeSubmoduleAsYinText(Module, Module, OutputStream)} instead.
      */
+    @Deprecated
     public static void writeModuleToOutputStream(final SchemaContext ctx, final Module module, final OutputStream str,
             final boolean emitInstantiated) throws XMLStreamException {
-        final XMLOutputFactory factory = XMLOutputFactory.newFactory();
-        final XMLStreamWriter xmlStreamWriter = factory.createXMLStreamWriter(str);
+        final XMLStreamWriter xmlStreamWriter = OUTPUT_FACTORY.createXMLStreamWriter(str);
         writeModuleToOutputStream(ctx, module, xmlStreamWriter, emitInstantiated);
         xmlStreamWriter.flush();
     }
@@ -131,4 +175,15 @@ public final class YinExportUtils {
         throw new IllegalArgumentException("Module " + moduleName + "does not exists in provided schema context");
     }
 
+    private static void writeReaderToOutput(final XMLEventReader reader, final OutputStream output)
+            throws XMLStreamException {
+        try {
+            final Transformer transformer = TRANSFORMER_FACTORY.newTransformer();
+            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
+            transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
+            transformer.transform(new StAXSource(reader), new StreamResult(output));
+        } catch (TransformerException e) {
+            throw new XMLStreamException("Failed to stream XML events", e);
+        }
+    }
 }
diff --git a/yang/yang-model-export/src/main/java/org/opendaylight/yangtools/yang/model/export/YinXMLEventReader.java b/yang/yang-model-export/src/main/java/org/opendaylight/yangtools/yang/model/export/YinXMLEventReader.java
new file mode 100644 (file)
index 0000000..92d05d0
--- /dev/null
@@ -0,0 +1,228 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.model.export;
+
+import static com.google.common.collect.Iterators.singletonIterator;
+import static com.google.common.collect.Iterators.transform;
+import static java.util.Collections.emptyIterator;
+import static java.util.Objects.requireNonNull;
+
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayDeque;
+import java.util.Arrays;
+import java.util.Deque;
+import java.util.Iterator;
+import java.util.Map.Entry;
+import java.util.NoSuchElementException;
+import java.util.Queue;
+import javax.xml.XMLConstants;
+import javax.xml.stream.XMLEventFactory;
+import javax.xml.stream.XMLEventReader;
+import javax.xml.stream.XMLStreamException;
+import javax.xml.stream.events.Attribute;
+import javax.xml.stream.events.Characters;
+import javax.xml.stream.events.EndElement;
+import javax.xml.stream.events.StartElement;
+import javax.xml.stream.events.XMLEvent;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition;
+
+final class YinXMLEventReader implements XMLEventReader {
+    private static final class OpenElement {
+        final Iterator<? extends DeclaredStatement<?>> children;
+        final QName name;
+
+        OpenElement(final Iterator<? extends DeclaredStatement<?>> children) {
+            this.children = requireNonNull(children);
+            this.name = null;
+        }
+
+        OpenElement(final QName name, final Iterator<? extends DeclaredStatement<?>> children) {
+            this.children = requireNonNull(children);
+            this.name = requireNonNull(name);
+        }
+    }
+
+    private final Deque<OpenElement> stack = new ArrayDeque<>(8);
+    private final Queue<XMLEvent> events = new ArrayDeque<>();
+    private final ModuleNamespaceContext namespaceContext;
+    private final XMLEventFactory eventFactory;
+
+    YinXMLEventReader(final XMLEventFactory eventFactory, final ModuleNamespaceContext namespaceContext,
+            final DeclaredStatement<?> root) {
+        this.eventFactory = requireNonNull(eventFactory);
+        this.namespaceContext = requireNonNull(namespaceContext);
+
+        events.add(eventFactory.createStartDocument(StandardCharsets.UTF_8.name()));
+
+        final StatementDefinition def = root.statementDefinition();
+        final QName name = def.getStatementName();
+
+        events.add(eventFactory.createStartElement(XMLConstants.DEFAULT_NS_PREFIX, name.getNamespace().toString(),
+            name.getLocalName(), singletonIterator(attribute(def.getArgumentName(), root.rawArgument())),
+            transform(namespaceContext.prefixesAndNamespaces().entrySet().iterator(),
+                e -> eventFactory.createNamespace(e.getKey(), e.getValue())),
+            namespaceContext));
+
+        stack.push(new OpenElement(name, root.declaredSubstatements().iterator()));
+    }
+
+    @Override
+    public XMLEvent next() {
+        XMLEvent event = events.poll();
+        if (event != null) {
+            return event;
+        }
+
+        nextStatement();
+        event = events.poll();
+        if (event == null) {
+            throw new NoSuchElementException("All events have been processed");
+        }
+        return event;
+    }
+
+    @Override
+    public XMLEvent nextEvent() {
+        return next();
+    }
+
+    @Override
+    public boolean hasNext() {
+        if (events.isEmpty()) {
+            nextStatement();
+            return events.isEmpty();
+        }
+        return true;
+    }
+
+    @Override
+    public XMLEvent peek() throws XMLStreamException {
+        if (events.isEmpty()) {
+            nextStatement();
+        }
+
+        return events.peek();
+    }
+
+    @Override
+    public String getElementText() throws XMLStreamException {
+        XMLEvent current = peek();
+        if (current == null) {
+            throw new XMLStreamException("End of event stream");
+        }
+        if (!(current instanceof StartElement)) {
+            throw new XMLStreamException("Current event is " + current);
+        }
+
+        current = next();
+        if (!(current instanceof Characters)) {
+            throw new XMLStreamException("Encountered non-text event " + current);
+        }
+        final String ret = ((Characters)current).getData();
+
+        current = next();
+        if (!(current instanceof EndElement)) {
+            throw new XMLStreamException("Encountered unexpected event " + current);
+        }
+        return ret;
+    }
+
+    @Override
+    public XMLEvent nextTag() throws XMLStreamException {
+        final XMLEvent next = next();
+        if (next instanceof Characters) {
+            throw new XMLStreamException("Significant characters encountered: " + next);
+        }
+        return next;
+    }
+
+    @Override
+    public Object getProperty(final String name) {
+        throw new IllegalArgumentException("Property " + name + " not supported");
+    }
+
+    @Override
+    public void close() {
+        events.clear();
+        stack.clear();
+    }
+
+    private Attribute attribute(final QName qname, final String value) {
+        final Entry<String, String> ns = namespaceContext.prefixAndNamespaceFor(qname.getModule());
+        return eventFactory.createAttribute(ns.getKey(), ns.getValue(), qname.getLocalName(), value);
+    }
+
+    private StartElement startElement(final QName qname) {
+        final Entry<String, String> ns = namespaceContext.prefixAndNamespaceFor(qname.getModule());
+        return eventFactory.createStartElement(ns.getKey(), ns.getValue(), qname.getLocalName(), emptyIterator(),
+            emptyIterator(), namespaceContext);
+    }
+
+    private EndElement endElement(final QName qname) {
+        final Entry<String, String> ns = namespaceContext.prefixAndNamespaceFor(qname.getModule());
+        return eventFactory.createEndElement(ns.getKey(), ns.getValue(), qname.getLocalName());
+    }
+
+    private void nextStatement() {
+        OpenElement current = stack.peek();
+        if (current == null) {
+            return;
+        }
+
+        do {
+            while (current.children.hasNext()) {
+                // We have to mind child statement source and not emit empty implicit children
+                final DeclaredStatement<?> child = current.children.next();
+                switch (child.getStatementSource()) {
+                    case CONTEXT:
+                        final Iterator<? extends DeclaredStatement<?>> it = child.declaredSubstatements().iterator();
+                        if (it.hasNext()) {
+                            current = new OpenElement(it);
+                        }
+                        break;
+                    case DECLARATION:
+                        addStatement(child);
+                        return;
+                    default:
+                        throw new IllegalStateException("Unhandled statement source " + child.getStatementSource());
+                }
+            }
+
+            if (current.name != null) {
+                events.add(endElement(current.name));
+            }
+            stack.pop();
+            if (stack.isEmpty()) {
+                events.add(eventFactory.createEndDocument());
+            }
+        } while (events.isEmpty());
+    }
+
+    private void addStatement(final DeclaredStatement<?> statement) {
+        final StatementDefinition def = statement.statementDefinition();
+        final QName name = def.getStatementName();
+        final QName argName = def.getArgumentName();
+        if (argName != null) {
+            if (def.isArgumentYinElement()) {
+                events.addAll(Arrays.asList(startElement(name), startElement(argName),
+                    eventFactory.createCharacters(statement.rawArgument()), endElement(argName)));
+            } else {
+                final Entry<String, String> ns = namespaceContext.prefixAndNamespaceFor(name.getModule());
+                events.add(eventFactory.createStartElement(ns.getKey(), ns.getValue(), name.getLocalName(),
+                    singletonIterator(attribute(argName, statement.rawArgument())), emptyIterator(), namespaceContext));
+            }
+        } else {
+            // No attributes: just emit a start
+            events.add(startElement(name));
+        }
+
+        stack.push(new OpenElement(name, statement.declaredSubstatements().iterator()));
+    }
+}
diff --git a/yang/yang-model-export/src/main/java/org/opendaylight/yangtools/yang/model/export/YinXMLEventReaderFactory.java b/yang/yang-model-export/src/main/java/org/opendaylight/yangtools/yang/model/export/YinXMLEventReaderFactory.java
new file mode 100644 (file)
index 0000000..f07c68e
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o. 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.model.export;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import javax.annotation.concurrent.ThreadSafe;
+import javax.xml.stream.Location;
+import javax.xml.stream.XMLEventFactory;
+import javax.xml.stream.XMLEventReader;
+import org.opendaylight.yangtools.yang.model.api.stmt.ModuleEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ModuleStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SubmoduleEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.SubmoduleStatement;
+
+/**
+ * Factory for creating {@link XMLEventReader} instances reporting events equivalent to reading a YIN document defining
+ * a specified {@link ModuleEffectiveStatement}.
+ */
+@Beta
+@ThreadSafe
+public final class YinXMLEventReaderFactory {
+    private static final Location DUMMY_LOCATION = new Location() {
+
+        @Override
+        public int getLineNumber() {
+            return -1;
+        }
+
+        @Override
+        public int getColumnNumber() {
+            return -1;
+        }
+
+        @Override
+        public int getCharacterOffset() {
+            return -1;
+        }
+
+        @Override
+        public String getPublicId() {
+            return null;
+        }
+
+        @Override
+        public String getSystemId() {
+            return null;
+        }
+    };
+
+    private static final YinXMLEventReaderFactory DEFAULT;
+
+    static {
+        final XMLEventFactory eventFactory = XMLEventFactory.newFactory();
+        eventFactory.setLocation(DUMMY_LOCATION);
+        DEFAULT = new YinXMLEventReaderFactory(eventFactory);
+    }
+
+    private final XMLEventFactory eventFactory;
+
+    private YinXMLEventReaderFactory(final XMLEventFactory eventFactory) {
+        this.eventFactory = requireNonNull(eventFactory);
+    }
+
+    /**
+     * Get the system-wide default instance, backed by system-wide default XMLEventFactory.
+     *
+     * @return Default instance.
+     */
+    public static YinXMLEventReaderFactory defaultInstance() {
+        return DEFAULT;
+    }
+
+    public static YinXMLEventReaderFactory ofEventFactory(final XMLEventFactory factory) {
+        return new YinXMLEventReaderFactory(factory);
+    }
+
+    /**
+     * Create a new XMLEventReader iterating of the YIN document equivalent of an effective module.
+     *
+     * @param module Effective module
+     * @return A new XMLEventReader.
+     * @throws NullPointerException if module is null
+     * @throws IllegalArgumentException if the specified module does not expose declared model
+     */
+    public final XMLEventReader createXMLEventReader(final ModuleEffectiveStatement module) {
+        final ModuleStatement declared = module.getDeclared();
+        checkArgument(declared != null, "Module %s does not expose declared model", module);
+
+        return new YinXMLEventReader(eventFactory, new ModuleNamespaceContext(module), declared);
+    }
+
+    /**
+     * Create a new XMLEventReader iterating of the YIN document equivalent of an effective submodule.
+     *
+     * @param module Effective module
+     * @param submodule Effective submodule
+     * @return A new XMLEventReader.
+     * @throws NullPointerException if any argument is null
+     * @throws IllegalArgumentException if the specified submodule does not expose declared model
+     */
+    public final XMLEventReader createXMLEventReader(final ModuleEffectiveStatement module,
+            final SubmoduleEffectiveStatement submodule) {
+        final SubmoduleStatement declared = submodule.getDeclared();
+        checkArgument(declared != null, "Submodule %s does not expose declared model", submodule);
+        return new YinXMLEventReader(eventFactory, new ModuleNamespaceContext(module), declared);
+    }
+}
diff --git a/yang/yang-model-export/src/test/java/org/opendaylight/yangtools/yang/model/export/AbstractYinExportTest.java b/yang/yang-model-export/src/test/java/org/opendaylight/yangtools/yang/model/export/AbstractYinExportTest.java
new file mode 100644 (file)
index 0000000..07c08ee
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2018 Pantheon Technologies, s.r.o..  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.model.export;
+
+import static org.junit.Assert.assertNotEquals;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.Collection;
+import javax.xml.stream.XMLStreamException;
+import org.custommonkey.xmlunit.Diff;
+import org.custommonkey.xmlunit.ElementNameAndAttributeQualifier;
+import org.custommonkey.xmlunit.XMLAssert;
+import org.custommonkey.xmlunit.XMLUnit;
+import org.opendaylight.yangtools.yang.model.api.Module;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+import org.w3c.dom.Document;
+import org.xml.sax.SAXException;
+
+abstract class AbstractYinExportTest {
+    final void exportYinModules(final String yangDir, final String yinDir) throws IOException, SAXException,
+            XMLStreamException {
+        final SchemaContext schemaContext = YangParserTestUtils.parseYangResourceDirectory(yangDir);
+        final Collection<Module> modules = schemaContext.getModules();
+        assertNotEquals(0, modules.size());
+
+        for (Module module : modules) {
+            readAndValidateModule(schemaContext, module, yinDir);
+
+            for (Module submodule : module.getSubmodules()) {
+                readAndValidateSubmodule(schemaContext, module, submodule, yinDir);
+            }
+        }
+    }
+
+    void validateOutput(final String yinDir, final String fileName, final String fileBody) throws IOException,
+            SAXException {
+        assertNotEquals(0, fileBody.length());
+        if (yinDir != null) {
+            final Document doc = YinExportTestUtils.loadDocument(yinDir + "/" + fileName);
+            assertXMLEquals(fileName, doc, fileBody);
+        }
+    }
+
+    private void readAndValidateModule(final SchemaContext schemaContext, final Module module, final String yinDir)
+            throws XMLStreamException, IOException, SAXException {
+        final String fileName = YinExportUtils.wellFormedYinName(module.getName(), module.getRevision());
+        validateOutput(yinDir, fileName, exportLegacy(schemaContext, module));
+        validateOutput(yinDir, fileName, export(module));
+    }
+
+    private void readAndValidateSubmodule(final SchemaContext schemaContext, final Module module,
+            final Module submodule, final String yinDir) throws XMLStreamException, IOException, SAXException {
+        final String fileName = YinExportUtils.wellFormedYinName(submodule.getName(), submodule.getRevision());
+        validateOutput(yinDir, fileName, exportLegacy(schemaContext, submodule));
+        validateOutput(yinDir, fileName, export(module, submodule));
+    }
+
+    private static String export(final Module module) throws XMLStreamException {
+        final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        YinExportUtils.writeModuleAsYinText(module, bos);
+        return new String(bos.toByteArray(), StandardCharsets.UTF_8);
+    }
+
+    private static String export(final Module module, final Module submodule) throws XMLStreamException {
+        final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        YinExportUtils.writeSubmoduleAsYinText(module, submodule, bos);
+        return new String(bos.toByteArray(), StandardCharsets.UTF_8);
+    }
+
+    private static String exportLegacy(final SchemaContext schemaContext, final Module module)
+            throws XMLStreamException {
+        final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+        YinExportUtils.writeModuleToOutputStream(schemaContext, module, bos);
+        return new String(bos.toByteArray(), StandardCharsets.UTF_8);
+    }
+
+    private static void assertXMLEquals(final String fileName, final Document expectedXMLDoc, final String output)
+            throws SAXException, IOException {
+        final String expected = YinExportTestUtils.toString(expectedXMLDoc.getDocumentElement());
+
+        XMLUnit.setIgnoreWhitespace(true);
+        XMLUnit.setNormalize(true);
+        XMLUnit.setNormalizeWhitespace(true);
+
+        final Diff diff = new Diff(expected, output);
+        diff.overrideElementQualifier(new ElementNameAndAttributeQualifier());
+        XMLAssert.assertXMLEqual(fileName, diff, true);
+    }
+}
index 872d76963b6ec024f613d4c129c6c45bd39e72d5..834550f16f1e94aa0a1836aa79a2cdca3afbc4a5 100644 (file)
@@ -7,71 +7,11 @@
  */
 package org.opendaylight.yangtools.yang.model.export;
 
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNotNull;
-
-import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.ImmutableSet.Builder;
-import java.io.BufferedOutputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.OutputStream;
-import org.custommonkey.xmlunit.Diff;
-import org.custommonkey.xmlunit.ElementNameAndAttributeQualifier;
-import org.custommonkey.xmlunit.XMLAssert;
-import org.custommonkey.xmlunit.XMLUnit;
 import org.junit.Test;
-import org.opendaylight.yangtools.yang.model.api.Module;
-import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.opendaylight.yangtools.yang.model.export.YinExportUtils;
-import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
-import org.w3c.dom.Document;
-import org.xml.sax.SAXException;
 
-public class Bug2444Test {
+public class Bug2444Test extends AbstractYinExportTest {
     @Test
     public void test() throws Exception {
-        final SchemaContext schema = YangParserTestUtils.parseYangResourceDirectory("/bugs/bug2444/yang");
-        assertNotNull(schema);
-
-        final ImmutableSet<Module> modulesAndSubmodules = getAllModulesAndSubmodules(schema);
-        for (final Module module : modulesAndSubmodules) {
-            final OutputStream byteArrayOutputStream = new ByteArrayOutputStream();
-            final BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(byteArrayOutputStream);
-            try {
-                YinExportUtils.writeModuleToOutputStream(schema, module, bufferedOutputStream);
-                final String output = byteArrayOutputStream.toString();
-                assertNotNull(output);
-                assertNotEquals(0, output.length());
-
-                final Document doc = YinExportTestUtils.loadDocument("/bugs/bug2444/yin", module);
-                assertXMLEquals(module.getName(), doc, output);
-            } finally {
-                byteArrayOutputStream.close();
-                bufferedOutputStream.close();
-            }
-        }
-    }
-
-    private static ImmutableSet<Module> getAllModulesAndSubmodules(final SchemaContext schema) {
-        final Builder<Module> builder = ImmutableSet.builder();
-        builder.addAll(schema.getModules());
-        for (final Module module : schema.getModules()) {
-            builder.addAll(module.getSubmodules());
-        }
-        return builder.build();
-    }
-
-    private static void assertXMLEquals(final String fileName, final Document expectedXMLDoc, final String output)
-            throws SAXException, IOException {
-        final String expected = YinExportTestUtils.toString(expectedXMLDoc.getDocumentElement());
-
-        XMLUnit.setIgnoreWhitespace(true);
-        XMLUnit.setNormalize(true);
-        XMLUnit.setNormalizeWhitespace(true);
-
-        final Diff diff = new Diff(expected, output);
-        diff.overrideElementQualifier(new ElementNameAndAttributeQualifier());
-        XMLAssert.assertXMLEqual(fileName, diff, true);
+        exportYinModules("/bugs/bug2444/yang", "/bugs/bug2444/yin");
     }
 }
index cbb01273b69ffb009dee30a4829dd48b65cb0965..1e424690a46c677058452c68375cf086fce1fb58 100644 (file)
@@ -7,35 +7,14 @@
  */
 package org.opendaylight.yangtools.yang.model.export;
 
-import static org.junit.Assert.assertNotNull;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.OutputStream;
+import java.io.IOException;
+import javax.xml.stream.XMLStreamException;
 import org.junit.Test;
-import org.opendaylight.yangtools.yang.model.api.Module;
-import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.opendaylight.yangtools.yang.model.export.YinExportUtils;
-import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
+import org.xml.sax.SAXException;
 
-public class Bug4504Test {
+public class Bug4504Test extends AbstractYinExportTest {
     @Test
-    public void test() throws Exception {
-        SchemaContext schema = YangParserTestUtils.parseYangResourceDirectory("/bugs/bug4504");
-        assertNotNull(schema);
-        final File outDir = new File("target/bug4504-export");
-        outDir.mkdirs();
-        for (final Module module : schema.getModules()) {
-            exportModule(schema, module, outDir);
-        }
-    }
-
-    private static File exportModule(final SchemaContext schemaContext, final Module module, final File outDir)
-            throws Exception {
-        final File outFile = new File(outDir, YinExportUtils.wellFormedYinName(module.getName(), module.getRevision()));
-        try (OutputStream output = new FileOutputStream(outFile)) {
-            YinExportUtils.writeModuleToOutputStream(schemaContext, module, output);
-        }
-        return outFile;
+    public void test() throws IOException, SAXException, XMLStreamException {
+        exportYinModules("/bugs/bug4504", null);
     }
 }
index 17ce3562797fa1a6fd2a4eafdf386208aff88015..2b151d15eaeb325939bf9676dc5ab1ed3024f910 100644 (file)
@@ -49,9 +49,9 @@ public class EffectiveSchemaContextEmitterTest {
 
         for (final Module module : schema.getModules()) {
             exportModule(schema, module, outDir);
-            final OutputStream byteArrayOutputStream = new ByteArrayOutputStream();
-            final BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(byteArrayOutputStream);
-            try {
+
+            try (OutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
+                try (BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(byteArrayOutputStream)) {
                 writeModuleToOutputStream(schema, module, bufferedOutputStream, false);
                 final String output = byteArrayOutputStream.toString();
                 assertNotNull(output);
@@ -59,9 +59,7 @@ public class EffectiveSchemaContextEmitterTest {
 
                 final Document doc = YinExportTestUtils.loadDocument("/bugs/bug2444/yin-effective-emitter", module);
                 assertXMLEquals(module.getName(), doc, output);
-            } finally {
-                byteArrayOutputStream.close();
-                bufferedOutputStream.close();
+                }
             }
         }
     }
index 791dd0c49cddb2839a11129ae5d8859243b0cc8d..f1c151d3abd54847d1f1a58860a7c90b9eab5de6 100644 (file)
@@ -8,55 +8,15 @@
 
 package org.opendaylight.yangtools.yang.model.export;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertNotEquals;
-import static org.junit.Assert.assertNotNull;
-
-import java.io.BufferedOutputStream;
-import java.io.ByteArrayOutputStream;
 import java.io.IOException;
-import java.io.OutputStream;
 import javax.xml.stream.XMLStreamException;
-import org.custommonkey.xmlunit.Diff;
-import org.custommonkey.xmlunit.ElementNameAndAttributeQualifier;
-import org.custommonkey.xmlunit.XMLAssert;
-import org.custommonkey.xmlunit.XMLUnit;
 import org.junit.Test;
-import org.opendaylight.yangtools.yang.model.api.Module;
-import org.opendaylight.yangtools.yang.model.api.SchemaContext;
-import org.opendaylight.yangtools.yang.model.export.YinExportUtils;
-import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils;
-import org.w3c.dom.Document;
 import org.xml.sax.SAXException;
 
-public class SchemaContextEmitterTest {
+public class SchemaContextEmitterTest extends AbstractYinExportTest {
 
     @Test
     public void testSchemaContextEmitter() throws IOException, XMLStreamException, SAXException {
-        final SchemaContext schemaContext = YangParserTestUtils.parseYangResourceDirectory(
-            "/schema-context-emitter-test");
-        assertNotNull(schemaContext);
-        assertEquals(1, schemaContext.getModules().size());
-
-        final OutputStream byteArrayOutputStream = new ByteArrayOutputStream();
-        final BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(byteArrayOutputStream);
-
-        for (final Module module : schemaContext.getModules()) {
-            YinExportUtils.writeModuleToOutputStream(schemaContext, module, bufferedOutputStream);
-        }
-
-        final String output = byteArrayOutputStream.toString();
-        assertNotNull(output);
-        assertNotEquals(0, output.length());
-
-        final Document doc = YinExportTestUtils.loadDocument("/schema-context-emitter-test/foo.yin");
-        final String expected = YinExportTestUtils.toString(doc.getDocumentElement());
-
-        XMLUnit.setIgnoreWhitespace(true);
-        XMLUnit.setNormalize(true);
-
-        final Diff diff = new Diff(expected, output);
-        diff.overrideElementQualifier(new ElementNameAndAttributeQualifier());
-        XMLAssert.assertXMLEqual(diff, true);
+        exportYinModules("/schema-context-emitter-test", "/schema-context-emitter-test");
     }
 }