Make model prefix handling optional 80/108080/3
authorRobert Varga <robert.varga@pantheon.tech>
Sat, 30 Sep 2023 00:22:32 +0000 (02:22 +0200)
committerRobert Varga <robert.varga@pantheon.tech>
Sun, 1 Oct 2023 15:22:33 +0000 (17:22 +0200)
Taking model-based prefixes into account is bound to change our output,
making the encoding a tad more dense. Make the feature selectable by
users as a first step, with an eye towards making it the default.

JIRA: YANGTOOLS-1544
Change-Id: Icedf67b408182e57a99d5d1f52f728c3f64c8657
Signed-off-by: Robert Varga <robert.varga@pantheon.tech>
16 files changed:
codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/IdentityrefXmlCodec.java
codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/InstanceIdentifierSerializer.java
codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/InstanceIdentifierXmlCodec.java
codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/NamespacePrefixes.java
codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/PreferredPrefixes.java
codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/SchemaAwareXMLStreamNormalizedNodeStreamWriter.java
codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/SchemaAwareXMLStreamWriterUtils.java
codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/SchemalessXMLStreamNormalizedNodeStreamWriter.java
codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/StreamWriterFacade.java
codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XMLStreamNormalizedNodeStreamWriter.java
codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlCodecFactory.java
codec/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/NamespacePrefixesTest.java
codec/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/PreferredPrefixesTest.java
codec/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlStreamUtilsTest.java
codec/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/YT1473Test.java
codec/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/YT1543Test.java

index 6b106af50a2e7bad8cebdf409c08fa80e9e58479..68a7c5fdc1a5a586ae06282bf2ed1773d098ad4d 100644 (file)
@@ -15,6 +15,7 @@ import javax.xml.namespace.NamespaceContext;
 import javax.xml.stream.XMLStreamException;
 import javax.xml.stream.XMLStreamWriter;
 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.XMLNamespace;
@@ -24,14 +25,14 @@ import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
 
 final class IdentityrefXmlCodec implements XmlCodec<QName> {
     private final @NonNull EffectiveModelContext context;
-    private final @NonNull PreferredPrefixes pref;
     private final @NonNull QNameModule parentModule;
+    private final @Nullable PreferredPrefixes pref;
 
-    IdentityrefXmlCodec(final EffectiveModelContext context, final PreferredPrefixes pref,
-            final QNameModule parentModule) {
+    IdentityrefXmlCodec(final EffectiveModelContext context, final QNameModule parentModule,
+            final @Nullable PreferredPrefixes pref) {
         this.context = requireNonNull(context);
-        this.pref = requireNonNull(pref);
         this.parentModule = requireNonNull(parentModule);
+        this.pref = pref;
     }
 
     @Override
@@ -58,11 +59,11 @@ final class IdentityrefXmlCodec implements XmlCodec<QName> {
 
     @Override
     public void writeValue(final XMLStreamWriter ctx, final QName value) throws XMLStreamException {
-        final var prefixes = new NamespacePrefixes(pref, ctx.getNamespaceContext());
+        final var prefixes = new NamespacePrefixes(ctx.getNamespaceContext(), pref);
         final var str = QNameCodecUtil.encodeQName(value, uri -> prefixes.encodePrefix(uri.getNamespace()));
 
-        for (var e : prefixes.emittedPrefixes()) {
-            ctx.writeNamespace(e.getValue(), e.getKey().toString());
+        for (var entry : prefixes.emittedPrefixes()) {
+            ctx.writeNamespace(entry.getValue(), entry.getKey().toString());
         }
         ctx.writeCharacters(str);
     }
index 36ded53757c9aa2e9a0906f35ad1c950b3dc7bef..6b6087bf436d54720a76c39981b0689c3830b78c 100644 (file)
@@ -10,6 +10,7 @@ package org.opendaylight.yangtools.yang.data.codec.xml;
 import java.util.List;
 import java.util.Map.Entry;
 import javax.xml.namespace.NamespaceContext;
+import org.eclipse.jdt.annotation.Nullable;
 import org.opendaylight.yangtools.yang.common.XMLNamespace;
 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
 import org.opendaylight.yangtools.yang.model.api.Module;
@@ -17,10 +18,10 @@ import org.opendaylight.yangtools.yang.model.api.Module;
 final class InstanceIdentifierSerializer extends AbstractInstanceIdentifierCodec {
     private final NamespacePrefixes prefixes;
 
-    InstanceIdentifierSerializer(final DataSchemaContextTree dataContextTree, final PreferredPrefixes pref,
-            final NamespaceContext nsContext) {
+    InstanceIdentifierSerializer(final DataSchemaContextTree dataContextTree,  final NamespaceContext nsContext,
+            final @Nullable PreferredPrefixes pref) {
         super(dataContextTree);
-        prefixes = new NamespacePrefixes(pref, nsContext);
+        prefixes = new NamespacePrefixes(nsContext, pref);
     }
 
     List<Entry<XMLNamespace, String>> emittedPrefixes() {
index f803f5483a233920df2eae0942607e1310d08b0f..c01601aab418077fb1c609fcfeb08b67291fef70 100644 (file)
@@ -13,17 +13,18 @@ import javax.xml.namespace.NamespaceContext;
 import javax.xml.stream.XMLStreamException;
 import javax.xml.stream.XMLStreamWriter;
 import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
 
 final class InstanceIdentifierXmlCodec implements XmlCodec<YangInstanceIdentifier> {
     private final @NonNull XmlCodecFactory codecFactory;
-    private final DataSchemaContextTree dataContextTree;
-    private final PreferredPrefixes pref;
+    private final @NonNull DataSchemaContextTree dataContextTree;
+    private final @Nullable PreferredPrefixes pref;
 
-    InstanceIdentifierXmlCodec(final XmlCodecFactory codecFactory, final PreferredPrefixes pref) {
+    InstanceIdentifierXmlCodec(final XmlCodecFactory codecFactory, final @Nullable PreferredPrefixes pref) {
         this.codecFactory = requireNonNull(codecFactory);
-        this.pref = requireNonNull(pref);
+        this.pref = pref;
         dataContextTree = DataSchemaContextTree.from(codecFactory.getEffectiveModelContext());
     }
 
@@ -40,7 +41,7 @@ final class InstanceIdentifierXmlCodec implements XmlCodec<YangInstanceIdentifie
 
     @Override
     public void writeValue(final XMLStreamWriter ctx, final YangInstanceIdentifier value) throws XMLStreamException {
-        final var serializer = new InstanceIdentifierSerializer(dataContextTree, pref, ctx.getNamespaceContext());
+        final var serializer = new InstanceIdentifierSerializer(dataContextTree, ctx.getNamespaceContext(), pref);
 
         final String str;
         try {
index 9e029757389873dbf36eec9889abe4cbf4fb8703..3b766f6179d053e8af919954d576ef664dafa873 100644 (file)
@@ -7,8 +7,6 @@
  */
 package org.opendaylight.yangtools.yang.data.codec.xml;
 
-import static java.util.Objects.requireNonNull;
-
 import com.google.common.annotations.VisibleForTesting;
 import com.google.common.collect.BiMap;
 import com.google.common.collect.HashBiMap;
@@ -19,6 +17,7 @@ import java.util.stream.Collectors;
 import javax.xml.XMLConstants;
 import javax.xml.namespace.NamespaceContext;
 import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
 import org.opendaylight.yangtools.concepts.Mutable;
 import org.opendaylight.yangtools.yang.common.XMLNamespace;
 
@@ -40,9 +39,9 @@ final class NamespacePrefixes implements Mutable {
     private final PreferredPrefixes pref;
     private final NamespaceContext context;
 
-    NamespacePrefixes(final PreferredPrefixes pref, final NamespaceContext context) {
-        this.pref = requireNonNull(pref);
+    NamespacePrefixes(final NamespaceContext context, final @Nullable PreferredPrefixes pref) {
         this.context = context;
+        this.pref = pref;
     }
 
     List<Entry<XMLNamespace, String>> emittedPrefixes() {
@@ -65,20 +64,30 @@ final class NamespacePrefixes implements Mutable {
             }
         }
 
-        prefix = pref.prefixForNamespace(namespace);
-        if (prefix == null) {
-            prefix = encode(counter++);
-        }
+        prefix = createPrefix(namespace);
+        emittedPrefixes.put(namespace, prefix);
+        return prefix;
+    }
 
-        while (alreadyUsedPrefix(prefix)) {
-            prefix = encode(counter++);
+    private @NonNull String createPrefix(final @NonNull XMLNamespace namespace) {
+        if (pref != null) {
+            final var prefix = pref.prefixForNamespace(namespace);
+            if (prefix != null) {
+                return prefix;
+            }
         }
 
-        emittedPrefixes.put(namespace, prefix);
+        String prefix;
+        do {
+            prefix = encode(counter++);
+        } while (alreadyUsedPrefix(prefix));
         return prefix;
     }
 
     private boolean alreadyUsedPrefix(final String prefix) {
+        if (pref != null && pref.isUsed(prefix)) {
+            return true;
+        }
         if (context == null) {
             return false;
         }
index ddf35512f3691bf5b003f02ed030e184d7e358a6..9730e7310fe8219dec6094794dc857e8f333040f 100644 (file)
@@ -10,15 +10,18 @@ package org.opendaylight.yangtools.yang.data.codec.xml;
 import static java.util.Objects.requireNonNull;
 
 import com.google.common.base.MoreObjects;
+import com.google.common.collect.ImmutableBiMap;
 import com.google.common.collect.Maps;
 import java.util.Map;
 import java.util.Optional;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
+import java.util.stream.Stream;
 import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.Nullable;
 import org.opendaylight.yangtools.yang.common.XMLNamespace;
 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
+import org.opendaylight.yangtools.yang.model.api.stmt.ModuleEffectiveStatement;
 
 /**
  * Prefixes preferred by an {@link EffectiveModelContext}. This acts as an advisory to {@link NamespacePrefixes} for
@@ -27,13 +30,11 @@ import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
  * with those ambiguities.
  */
 abstract sealed class PreferredPrefixes {
-    private static final class Precomputed extends PreferredPrefixes {
-        static final @NonNull Precomputed EMPTY = new Precomputed(Map.of());
-
-        private final Map<XMLNamespace, String> mappings;
+    static final class Precomputed extends PreferredPrefixes {
+        private final ImmutableBiMap<XMLNamespace, String> mappings;
 
         Precomputed(final Map<XMLNamespace, String> mappings) {
-            this.mappings = requireNonNull(mappings);
+            this.mappings = ImmutableBiMap.copyOf(mappings);
         }
 
         @Override
@@ -41,6 +42,11 @@ abstract sealed class PreferredPrefixes {
             return mappings.get(namespace);
         }
 
+        @Override
+        boolean isUsed(final String prefix) {
+            return mappings.inverse().containsKey(prefix);
+        }
+
         @Override
         Map<XMLNamespace, ?> mappings() {
             return mappings;
@@ -67,6 +73,11 @@ abstract sealed class PreferredPrefixes {
             return modules.hasNext() ? loadPrefix(namespace, modules.next().prefix().argument()) : null;
         }
 
+        @Override
+        boolean isUsed(final String prefix) {
+            return modulesForPrefix(prefix).findAny().isPresent();
+        }
+
         /**
          * Completely populate known mappings and return an optimized version equivalent of this object.
          *
@@ -76,8 +87,8 @@ abstract sealed class PreferredPrefixes {
             for (var module : modelContext.getModuleStatements().values()) {
                 prefixForNamespace(module.namespace().argument());
             }
-            return new Precomputed(Map.copyOf(
-                Maps.transformValues(Maps.filterValues(mappings, Optional::isPresent), Optional::orElseThrow)));
+            return new Precomputed(
+                Maps.transformValues(Maps.filterValues(mappings, Optional::isPresent), Optional::orElseThrow));
         }
 
         @Override
@@ -93,15 +104,13 @@ abstract sealed class PreferredPrefixes {
 
         // Validate that all modules which have the same prefix have also the name namespace
         private boolean isValidMapping(final XMLNamespace namespace, final String prefix) {
-            if (startsWithXml(prefix)) {
-                return false;
-            }
-            for (var module : modelContext.getModuleStatements().values()) {
-                if (prefix.equals(module.prefix().argument()) && !namespace.equals(module.namespace().argument())) {
-                    return false;
-                }
-            }
-            return true;
+            return !startsWithXml(prefix) && modulesForPrefix(prefix)
+                .allMatch(module -> namespace.equals(module.namespace().argument()));
+        }
+
+        private Stream<ModuleEffectiveStatement> modulesForPrefix(final String prefix) {
+            return modelContext.getModuleStatements().values().stream()
+                .filter(module -> prefix.equals(module.prefix().argument()));
         }
 
         // https://www.w3.org/TR/xml-names/#xmlReserved
@@ -126,16 +135,15 @@ abstract sealed class PreferredPrefixes {
         // Hidden on purpose
     }
 
-    static @NonNull PreferredPrefixes empty() {
-        return Precomputed.EMPTY;
-    }
-
     abstract @Nullable String prefixForNamespace(@NonNull XMLNamespace namespace);
 
+    abstract boolean isUsed(String prefix);
+
     @Override
     public final String toString() {
         return MoreObjects.toStringHelper(this).add("mappings", mappings()).toString();
     }
 
     abstract Map<XMLNamespace, ?> mappings();
+
 }
index fe285af1a07da522f6dedf54c3920e6670c322e0..9bcf452041c1b8c78b1a9aca0ffeb987746b66b3 100644 (file)
@@ -16,6 +16,7 @@ import javax.xml.stream.XMLStreamException;
 import javax.xml.stream.XMLStreamWriter;
 import javax.xml.transform.dom.DOMSource;
 import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
 import org.opendaylight.yangtools.rfc7952.model.api.AnnotationSchemaNode;
 import org.opendaylight.yangtools.yang.common.AnnotationName;
 import org.opendaylight.yangtools.yang.common.QName;
@@ -39,16 +40,18 @@ final class SchemaAwareXMLStreamNormalizedNodeStreamWriter
     private final NormalizedNodeStreamWriterStack tracker;
     private final SchemaAwareXMLStreamWriterUtils streamUtils;
 
-    private SchemaAwareXMLStreamNormalizedNodeStreamWriter(final PreferredPrefixes pref, final XMLStreamWriter writer,
-            final EffectiveModelContext modelContext, final NormalizedNodeStreamWriterStack tracker) {
-        super(pref, writer);
+    private SchemaAwareXMLStreamNormalizedNodeStreamWriter(final XMLStreamWriter writer,
+            final EffectiveModelContext modelContext, final NormalizedNodeStreamWriterStack tracker,
+            final @Nullable PreferredPrefixes pref) {
+        super(writer, pref);
         this.tracker = requireNonNull(tracker);
         streamUtils = new SchemaAwareXMLStreamWriterUtils(modelContext, pref);
     }
 
     SchemaAwareXMLStreamNormalizedNodeStreamWriter(final XMLStreamWriter writer,
-            final EffectiveModelContext modelContext, final NormalizedNodeStreamWriterStack tracker) {
-        this(new PreferredPrefixes.Shared(modelContext), writer, modelContext, tracker);
+            final EffectiveModelContext modelContext, final NormalizedNodeStreamWriterStack tracker,
+            final boolean modelPrefixes) {
+        this(writer, modelContext, tracker, modelPrefixes ? new PreferredPrefixes.Shared(modelContext) : null);
     }
 
     @Override
index fd7f0e313ee09d4467b77b1ad506e50e3d8d94c7..4710b33e262280706621bdb3ddbc323659ccb8aa 100644 (file)
@@ -11,17 +11,18 @@ import static java.util.Objects.requireNonNull;
 
 import javax.xml.stream.XMLStreamException;
 import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.util.DataSchemaContextTree;
 import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext;
 
 final class SchemaAwareXMLStreamWriterUtils extends XMLStreamWriterUtils {
     private final @NonNull EffectiveModelContext modelContext;
-    private final @NonNull PreferredPrefixes pref;
+    private final @Nullable PreferredPrefixes pref;
 
-    SchemaAwareXMLStreamWriterUtils(final EffectiveModelContext modelContext, final PreferredPrefixes pref) {
+    SchemaAwareXMLStreamWriterUtils(final EffectiveModelContext modelContext, final @Nullable PreferredPrefixes pref) {
         this.modelContext = requireNonNull(modelContext);
-        this.pref = requireNonNull(pref);
+        this.pref = pref;
     }
 
     @NonNull EffectiveModelContext modelContext() {
@@ -31,8 +32,8 @@ final class SchemaAwareXMLStreamWriterUtils extends XMLStreamWriterUtils {
     @Override
     String encodeInstanceIdentifier(final ValueWriter writer, final YangInstanceIdentifier value)
             throws XMLStreamException {
-        final var serializer = new InstanceIdentifierSerializer(DataSchemaContextTree.from(modelContext), pref,
-            writer.getNamespaceContext());
+        final var serializer = new InstanceIdentifierSerializer(DataSchemaContextTree.from(modelContext),
+            writer.getNamespaceContext(), pref);
         final var str = serializer.serialize(value);
         for (var entry : serializer.emittedPrefixes()) {
             writer.writeNamespace(entry.getValue(), entry.getKey().toString());
index 03ca5b853de03485f1393ef2239a32d0266cfd07..3279aff1b4a5649e988049be272286de0be2b901 100644 (file)
@@ -36,7 +36,7 @@ final class SchemalessXMLStreamNormalizedNodeStreamWriter extends XMLStreamNorma
     private final Deque<NodeType> nodeTypeStack = new ArrayDeque<>();
 
     SchemalessXMLStreamNormalizedNodeStreamWriter(final XMLStreamWriter writer) {
-        super(PreferredPrefixes.empty(), writer);
+        super(writer, null);
     }
 
     @Override
index 9482e557e19fc6a5fdaff44cfe641db92a2ece48..ce4a9c7763b6dbf3285aa661ba727af53390eb29 100644 (file)
@@ -20,6 +20,7 @@ import javax.xml.stream.XMLStreamConstants;
 import javax.xml.stream.XMLStreamException;
 import javax.xml.stream.XMLStreamReader;
 import javax.xml.stream.XMLStreamWriter;
+import org.eclipse.jdt.annotation.Nullable;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.common.XMLNamespace;
 import org.opendaylight.yangtools.yang.data.api.schema.NormalizedAnydata;
@@ -43,9 +44,9 @@ final class StreamWriterFacade extends ValueWriter {
     // namespace declarations or value.
     private QName openElement;
 
-    StreamWriterFacade(final PreferredPrefixes pref, final XMLStreamWriter writer) {
+    StreamWriterFacade(final XMLStreamWriter writer, final @Nullable PreferredPrefixes pref) {
         this.writer = requireNonNull(writer);
-        prefixes = new NamespacePrefixes(pref, writer.getNamespaceContext());
+        prefixes = new NamespacePrefixes(writer.getNamespaceContext(), pref);
     }
 
     void writeCharacters(final String text) throws XMLStreamException {
index cd24490928593cb6d07d628a5e4b8418a9dd9562..e8f9149dd9119df4765c9c7a558a970a2554f1db 100644 (file)
@@ -58,8 +58,8 @@ public abstract sealed class XMLStreamNormalizedNodeStreamWriter<T>
 
     private final @NonNull StreamWriterFacade facade;
 
-    XMLStreamNormalizedNodeStreamWriter(final PreferredPrefixes pref, final XMLStreamWriter writer) {
-        facade = new StreamWriterFacade(pref, writer);
+    XMLStreamNormalizedNodeStreamWriter(final XMLStreamWriter writer, final @Nullable PreferredPrefixes pref) {
+        facade = new StreamWriterFacade(writer, pref);
     }
 
     /**
@@ -71,8 +71,21 @@ public abstract sealed class XMLStreamNormalizedNodeStreamWriter<T>
      */
     public static @NonNull NormalizedNodeStreamWriter create(final XMLStreamWriter writer,
             final EffectiveModelContext context) {
+        return create(writer, context, false);
+    }
+
+    /**
+     * Create a new writer with the specified context as its root.
+     *
+     * @param writer Output {@link XMLStreamWriter}
+     * @param context Associated {@link EffectiveModelContext}.
+     * @param preferPrefixes prefer prefixes known to {@code context}
+     * @return A new {@link NormalizedNodeStreamWriter}
+     */
+    public static @NonNull NormalizedNodeStreamWriter create(final XMLStreamWriter writer,
+            final EffectiveModelContext context, final boolean preferPrefixes) {
         return new SchemaAwareXMLStreamNormalizedNodeStreamWriter(writer, context,
-            NormalizedNodeStreamWriterStack.of(context));
+            NormalizedNodeStreamWriterStack.of(context), preferPrefixes);
     }
 
     /**
@@ -84,8 +97,21 @@ public abstract sealed class XMLStreamNormalizedNodeStreamWriter<T>
      */
     public static @NonNull NormalizedNodeStreamWriter create(final XMLStreamWriter writer,
             final EffectiveStatementInference inference) {
+        return create(writer, inference, false);
+    }
+
+    /**
+     * Create a new writer with the specified context and rooted at the specified node.
+     *
+     * @param writer Output {@link XMLStreamWriter}
+     * @param inference root node inference
+     * @param preferPrefixes prefer prefixes known to {@code context}
+     * @return A new {@link NormalizedNodeStreamWriter}
+     */
+    public static @NonNull NormalizedNodeStreamWriter create(final XMLStreamWriter writer,
+            final EffectiveStatementInference inference, final boolean preferPrefixes) {
         return new SchemaAwareXMLStreamNormalizedNodeStreamWriter(writer, inference.getEffectiveModelContext(),
-            NormalizedNodeStreamWriterStack.of(inference));
+            NormalizedNodeStreamWriterStack.of(inference), preferPrefixes);
     }
 
     /**
@@ -98,9 +124,23 @@ public abstract sealed class XMLStreamNormalizedNodeStreamWriter<T>
      */
     public static @NonNull NormalizedNodeStreamWriter create(final XMLStreamWriter writer,
             final EffectiveModelContext context, final @Nullable Absolute path) {
+        return create(writer, context, path, false);
+    }
+
+    /**
+     * Create a new writer with the specified context and rooted in the specified schema path.
+     *
+     * @param writer Output {@link XMLStreamWriter}
+     * @param context Associated {@link EffectiveModelContext}.
+     * @param path path
+     * @param preferPrefixes prefer prefixes known to {@code context}
+     * @return A new {@link NormalizedNodeStreamWriter}
+     */
+    public static @NonNull NormalizedNodeStreamWriter create(final XMLStreamWriter writer,
+            final EffectiveModelContext context, final @Nullable Absolute path, final boolean preferPrefixes) {
         return path == null ? create(writer, context)
             : new SchemaAwareXMLStreamNormalizedNodeStreamWriter(writer, context,
-                NormalizedNodeStreamWriterStack.of(context, path));
+                NormalizedNodeStreamWriterStack.of(context, path), preferPrefixes);
     }
 
     /**
@@ -113,8 +153,22 @@ public abstract sealed class XMLStreamNormalizedNodeStreamWriter<T>
      */
     public static @NonNull NormalizedNodeStreamWriter create(final XMLStreamWriter writer,
             final EffectiveModelContext context, final YangInstanceIdentifier path) {
+        return create(writer, context, path, false);
+    }
+
+    /**
+     * Create a new writer with the specified context and rooted in the specified {@link YangInstanceIdentifier}.
+     *
+     * @param writer Output {@link XMLStreamWriter}
+     * @param context Associated {@link EffectiveModelContext}.
+     * @param path path
+     * @param preferPrefixes prefer prefixes known to {@code context}
+     * @return A new {@link NormalizedNodeStreamWriter}
+     */
+    public static @NonNull NormalizedNodeStreamWriter create(final XMLStreamWriter writer,
+            final EffectiveModelContext context, final YangInstanceIdentifier path, final boolean preferPrefixes) {
         return new SchemaAwareXMLStreamNormalizedNodeStreamWriter(writer, context,
-            NormalizedNodeStreamWriterStack.of(context, path));
+            NormalizedNodeStreamWriterStack.of(context, path), preferPrefixes);
     }
 
     /**
@@ -127,8 +181,22 @@ public abstract sealed class XMLStreamNormalizedNodeStreamWriter<T>
      */
     public static @NonNull NormalizedNodeStreamWriter forInputOf(final XMLStreamWriter writer,
             final EffectiveModelContext context, final Absolute operationPath) {
+        return forInputOf(writer, context, operationPath, false);
+    }
+
+    /**
+     * Create a new writer with the specified context and rooted in the specified operation's input.
+     *
+     * @param writer Output {@link XMLStreamWriter}
+     * @param context Associated {@link EffectiveModelContext}.
+     * @param operationPath Parent operation (RPC or action) path.
+     * @param preferPrefixes prefer prefixes known to {@code context}
+     * @return A new {@link NormalizedNodeStreamWriter}
+     */
+    public static @NonNull NormalizedNodeStreamWriter forInputOf(final XMLStreamWriter writer,
+            final EffectiveModelContext context, final Absolute operationPath, final boolean preferPrefixes) {
         return forOperation(writer, context, operationPath,
-            YangConstants.operationInputQName(operationPath.lastNodeIdentifier().getModule()));
+            YangConstants.operationInputQName(operationPath.lastNodeIdentifier().getModule()), preferPrefixes);
     }
 
     /**
@@ -141,14 +209,30 @@ public abstract sealed class XMLStreamNormalizedNodeStreamWriter<T>
      */
     public static @NonNull NormalizedNodeStreamWriter forOutputOf(final XMLStreamWriter writer,
             final EffectiveModelContext context, final Absolute operationPath) {
+        return forOutputOf(writer, context, operationPath, false);
+    }
+
+    /**
+     * Create a new writer with the specified context and rooted in the specified operation's output.
+     *
+     * @param writer Output {@link XMLStreamWriter}
+     * @param context Associated {@link EffectiveModelContext}.
+     * @param operationPath Parent operation (RPC or action) path.
+     * @param preferPrefixes prefer prefixes known to {@code context}
+     * @return A new {@link NormalizedNodeStreamWriter}
+     */
+    public static @NonNull NormalizedNodeStreamWriter forOutputOf(final XMLStreamWriter writer,
+            final EffectiveModelContext context, final Absolute operationPath, final boolean preferPrefixes) {
         return forOperation(writer, context, operationPath,
-            YangConstants.operationOutputQName(operationPath.lastNodeIdentifier().getModule()));
+            YangConstants.operationOutputQName(operationPath.lastNodeIdentifier().getModule()),
+            preferPrefixes);
     }
 
     private static @NonNull NormalizedNodeStreamWriter forOperation(final XMLStreamWriter writer,
-            final EffectiveModelContext context, final Absolute operationPath, final QName qname) {
+            final EffectiveModelContext context, final Absolute operationPath, final QName qname,
+            final boolean preferPrefixes) {
         return new SchemaAwareXMLStreamNormalizedNodeStreamWriter(writer, context,
-            NormalizedNodeStreamWriterStack.ofOperation(context, operationPath, qname));
+            NormalizedNodeStreamWriterStack.ofOperation(context, operationPath, qname), preferPrefixes);
     }
 
     /**
index bbb5e2686d2dc75ea4068725dcb9da358b9ed005..ebbe8c2add436aa2196be3d86f4462f336b6216f 100644 (file)
@@ -11,6 +11,7 @@ import static java.util.Objects.requireNonNull;
 
 import java.util.List;
 import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.Nullable;
 import org.opendaylight.yangtools.yang.common.QNameModule;
 import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
 import org.opendaylight.yangtools.yang.data.api.schema.MountPointContext;
@@ -48,14 +49,14 @@ import org.opendaylight.yangtools.yang.model.api.type.UnknownTypeDefinition;
  * A thread-safe factory for instantiating {@link XmlCodec}s.
  */
 public final class XmlCodecFactory extends AbstractCodecFactory<XmlCodec<?>> {
-    private final @NonNull MountPointContext mountCtx;
-    private final @NonNull PreferredPrefixes pref;
     private final @NonNull InstanceIdentifierXmlCodec instanceIdentifierCodec;
+    private final @NonNull MountPointContext mountCtx;
+    private final @Nullable PreferredPrefixes pref;
 
-    private XmlCodecFactory(final MountPointContext mountCtx) {
+    private XmlCodecFactory(final MountPointContext mountCtx, final boolean modelPrefixes) {
         super(mountCtx.getEffectiveModelContext(), new SharedCodecCache<>());
         this.mountCtx = requireNonNull(mountCtx);
-        pref = new PreferredPrefixes.Shared(getEffectiveModelContext());
+        pref = modelPrefixes ? new PreferredPrefixes.Shared(getEffectiveModelContext()) : null;
         instanceIdentifierCodec = new InstanceIdentifierXmlCodec(this, pref);
     }
 
@@ -70,7 +71,18 @@ public final class XmlCodecFactory extends AbstractCodecFactory<XmlCodec<?>> {
      * @return A codec factory instance.
      */
     public static XmlCodecFactory create(final MountPointContext context) {
-        return new XmlCodecFactory(context);
+        return create(context, false);
+    }
+
+    /**
+     * Instantiate a new codec factory attached to a particular context.
+     *
+     * @param context MountPointContext to which the factory should be bound
+     * @param preferPrefixes prefer prefixes known to {@code context}
+     * @return A codec factory instance.
+     */
+    public static XmlCodecFactory create(final MountPointContext context, final boolean preferPrefixes) {
+        return new XmlCodecFactory(context, preferPrefixes);
     }
 
     /**
@@ -80,7 +92,18 @@ public final class XmlCodecFactory extends AbstractCodecFactory<XmlCodec<?>> {
      * @return A codec factory instance.
      */
     public static XmlCodecFactory create(final EffectiveModelContext context) {
-        return create(MountPointContext.of(context));
+        return create(context, false);
+    }
+
+    /**
+     * Instantiate a new codec factory attached to a particular context.
+     *
+     * @param context SchemaContext to which the factory should be bound
+     * @param preferPrefixes prefer prefixes known to {@code context}
+     * @return A codec factory instance.
+     */
+    public static XmlCodecFactory create(final EffectiveModelContext context, final boolean preferPrefixes) {
+        return create(MountPointContext.of(requireNonNull(context)), preferPrefixes);
     }
 
     @Override
@@ -110,7 +133,7 @@ public final class XmlCodecFactory extends AbstractCodecFactory<XmlCodec<?>> {
 
     @Override
     protected XmlCodec<?> identityRefCodec(final IdentityrefTypeDefinition type, final QNameModule module) {
-        return new IdentityrefXmlCodec(getEffectiveModelContext(), pref, module);
+        return new IdentityrefXmlCodec(getEffectiveModelContext(), module, pref);
     }
 
     @Override
index d6fb28535507529e8c75f5665eb9a61b8899305d..69980a97ec3a204ae9151abf0fdda973b24c1c4f 100644 (file)
@@ -41,7 +41,7 @@ class NamespacePrefixesTest {
 
     @Test
     void testQNameWithPrefix() {
-        final var a = new NamespacePrefixes(PreferredPrefixes.empty(), null);
+        final var a = new NamespacePrefixes(null, null);
 
         final var allGenerated = new ArrayList<String>();
         for (int i = 0; i < MAX_COUNTER; i++) {
@@ -61,7 +61,7 @@ class NamespacePrefixesTest {
 
     @Test
     void test2QNames1Namespace() {
-        final var a = new NamespacePrefixes(PreferredPrefixes.empty(), null);
+        final var a = new NamespacePrefixes(null, null);
 
         final var uri = XMLNamespace.of("localhost");
 
@@ -71,7 +71,7 @@ class NamespacePrefixesTest {
 
     @Test
     void testQNameNoPrefix() {
-        final var a = new NamespacePrefixes(PreferredPrefixes.empty(), null);
+        final var a = new NamespacePrefixes(null, new PreferredPrefixes.Precomputed(Map.of()));
 
         final var uri = XMLNamespace.of("localhost");
         final var second = XMLNamespace.of("second");
index db4c9b710bb34259970eec7330eb91492668a9cc..f6ec9c92e7e302859e5716d33ed5054075c13e80 100644 (file)
@@ -9,6 +9,7 @@ package org.opendaylight.yangtools.yang.data.codec.xml;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import java.util.Map;
 import java.util.Optional;
@@ -56,7 +57,10 @@ class PreferredPrefixesTest {
         final var prefixes = new PreferredPrefixes.Shared(context);
         assertEquals("f", prefixes.prefixForNamespace(FOONS));
         assertEquals("Shared{mappings={foons=Optional[f]}}", prefixes.toString());
-        assertEquals("Precomputed{mappings={foons=f}}", prefixes.toPrecomputed().toString());
+
+        final var precomputed = prefixes.toPrecomputed();
+        assertEquals("f", precomputed.prefixForNamespace(FOONS));
+        assertEquals("Precomputed{mappings={foons=f}}", precomputed.toString());
     }
 
     @Test
@@ -87,4 +91,18 @@ class PreferredPrefixesTest {
             XMLNamespace.of("quxns"), "xmx",
             XMLNamespace.of("xyzzyns"), "xyz"), new PreferredPrefixes.Shared(context).toPrecomputed().mappings());
     }
+
+    @Test
+    void ignorePrefixesStartingWithXML() {
+        final var prefixes = new PreferredPrefixes.Shared(YangParserTestUtils.parseYang("""
+            module foo {
+              namespace foons;
+              prefix a;
+            }"""));
+        assertTrue(prefixes.isUsed("a"));
+        assertEquals("Shared{mappings={}}", prefixes.toString());
+        final var precomputed = prefixes.toPrecomputed();
+        assertTrue(precomputed.isUsed("a"));
+        assertEquals("Precomputed{mappings={foons=a}}", precomputed.toString());
+    }
 }
index 2c512635972ce50333f5e5ef5ae0028664ab29ab..c01e948b5f75f0ac83186797f422c81ae4f93933 100644 (file)
@@ -73,7 +73,7 @@ public class XmlStreamUtilsTest {
 
         String xmlAsString = createXml(writer -> {
             writer.writeStartElement("element");
-            final var facade = new StreamWriterFacade(pref, writer);
+            final var facade = new StreamWriterFacade(writer, pref);
             facade.writeCharacters(XMLStreamWriterUtils.encode(facade, QName.create(parent, "identity"), parent));
             facade.flush();
             writer.writeEndElement();
@@ -83,7 +83,7 @@ public class XmlStreamUtilsTest {
 
         xmlAsString = createXml(writer -> {
             writer.writeStartElement("elementDifferent");
-            final var facade = new StreamWriterFacade(pref, writer);
+            final var facade = new StreamWriterFacade(writer, pref);
             facade.writeCharacters(XMLStreamWriterUtils.encode(facade, QName.create("different:namespace", "identity"),
                 parent));
             facade.flush();
index 5c1aa55c10ff5001eb8ff4dc572d2b12d6f24266..44944e85e7472053347c7c2946f1e12153674e75 100644 (file)
@@ -135,7 +135,7 @@ class YT1473Test {
         final var baz = assertInstanceOf(ListSchemaNode.class, modelContext.getDataChildByName(FOO_BAZ));
         final var id = assertInstanceOf(LeafSchemaNode.class, baz.getDataChildByName(FOO_ID));
         final var type = assertInstanceOf(InstanceIdentifierTypeDefinition.class, id.getType());
-        CODEC = XmlCodecFactory.create(modelContext).instanceIdentifierCodec(type);
+        CODEC = XmlCodecFactory.create(modelContext, true).instanceIdentifierCodec(type);
     }
 
     @AfterAll
index 4cd307d01e5ba797c34814143c19f8a04a794a15..bba8c3ccf225b1f410f7d37cbd4e16b202a24c01 100644 (file)
@@ -67,7 +67,7 @@ class YT1543Test {
     void nestedInstanceIdentifierInDocument() throws Exception {
         final var stringWriter = new StringWriter();
         try (var xmlWriter = XMLStreamNormalizedNodeStreamWriter.create(
-                TestFactories.DEFAULT_OUTPUT_FACTORY.createXMLStreamWriter(stringWriter), MODEL_CONTEXT)) {
+                TestFactories.DEFAULT_OUTPUT_FACTORY.createXMLStreamWriter(stringWriter), MODEL_CONTEXT, true)) {
             try (var nnWriter = NormalizedNodeWriter.forStreamWriter(xmlWriter)) {
                 // Contrived: we have a document for foo's 'foo' container, with 'leaf' pointing to an instance of bar's
                 //            'bar' list item, whose key points to baz's 'baz' container.
@@ -91,7 +91,7 @@ class YT1543Test {
         xmlWriter.writeStartElement(XMLConstants.DEFAULT_NS_PREFIX, "foo", "foons");
         xmlWriter.writeDefaultNamespace("foons");
         xmlWriter.writeStartElement("leaf");
-        XmlCodecFactory.create(MODEL_CONTEXT).instanceIdentifierCodec().writeValue(xmlWriter, IID);
+        XmlCodecFactory.create(MODEL_CONTEXT, true).instanceIdentifierCodec().writeValue(xmlWriter, IID);
         xmlWriter.writeEndElement();
         xmlWriter.writeEndElement();
         xmlWriter.close();