From: Robert Varga Date: Sat, 30 Sep 2023 00:22:32 +0000 (+0200) Subject: Make model prefix handling optional X-Git-Tag: v11.0.3~4 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=commitdiff_plain;h=9d64ed881c503da6edea8b14d9ecd763a586b4c3;p=yangtools.git Make model prefix handling optional 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 --- diff --git a/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/IdentityrefXmlCodec.java b/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/IdentityrefXmlCodec.java index 6b106af50a..68a7c5fdc1 100644 --- a/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/IdentityrefXmlCodec.java +++ b/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/IdentityrefXmlCodec.java @@ -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 { 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 { @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); } diff --git a/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/InstanceIdentifierSerializer.java b/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/InstanceIdentifierSerializer.java index 36ded53757..6b6087bf43 100644 --- a/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/InstanceIdentifierSerializer.java +++ b/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/InstanceIdentifierSerializer.java @@ -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> emittedPrefixes() { diff --git a/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/InstanceIdentifierXmlCodec.java b/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/InstanceIdentifierXmlCodec.java index f803f5483a..c01601aab4 100644 --- a/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/InstanceIdentifierXmlCodec.java +++ b/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/InstanceIdentifierXmlCodec.java @@ -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 { 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> 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; } diff --git a/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/PreferredPrefixes.java b/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/PreferredPrefixes.java index ddf35512f3..9730e7310f 100644 --- a/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/PreferredPrefixes.java +++ b/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/PreferredPrefixes.java @@ -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 mappings; + static final class Precomputed extends PreferredPrefixes { + private final ImmutableBiMap mappings; Precomputed(final Map 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 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 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 mappings(); + } diff --git a/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/SchemaAwareXMLStreamNormalizedNodeStreamWriter.java b/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/SchemaAwareXMLStreamNormalizedNodeStreamWriter.java index fe285af1a0..9bcf452041 100644 --- a/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/SchemaAwareXMLStreamNormalizedNodeStreamWriter.java +++ b/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/SchemaAwareXMLStreamNormalizedNodeStreamWriter.java @@ -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 diff --git a/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/SchemaAwareXMLStreamWriterUtils.java b/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/SchemaAwareXMLStreamWriterUtils.java index fd7f0e313e..4710b33e26 100644 --- a/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/SchemaAwareXMLStreamWriterUtils.java +++ b/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/SchemaAwareXMLStreamWriterUtils.java @@ -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()); diff --git a/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/SchemalessXMLStreamNormalizedNodeStreamWriter.java b/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/SchemalessXMLStreamNormalizedNodeStreamWriter.java index 03ca5b853d..3279aff1b4 100644 --- a/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/SchemalessXMLStreamNormalizedNodeStreamWriter.java +++ b/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/SchemalessXMLStreamNormalizedNodeStreamWriter.java @@ -36,7 +36,7 @@ final class SchemalessXMLStreamNormalizedNodeStreamWriter extends XMLStreamNorma private final Deque nodeTypeStack = new ArrayDeque<>(); SchemalessXMLStreamNormalizedNodeStreamWriter(final XMLStreamWriter writer) { - super(PreferredPrefixes.empty(), writer); + super(writer, null); } @Override diff --git a/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/StreamWriterFacade.java b/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/StreamWriterFacade.java index 9482e557e1..ce4a9c7763 100644 --- a/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/StreamWriterFacade.java +++ b/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/StreamWriterFacade.java @@ -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 { diff --git a/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XMLStreamNormalizedNodeStreamWriter.java b/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XMLStreamNormalizedNodeStreamWriter.java index cd24490928..e8f9149dd9 100644 --- a/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XMLStreamNormalizedNodeStreamWriter.java +++ b/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XMLStreamNormalizedNodeStreamWriter.java @@ -58,8 +58,8 @@ public abstract sealed class XMLStreamNormalizedNodeStreamWriter 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 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); } /** diff --git a/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlCodecFactory.java b/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlCodecFactory.java index bbb5e2686d..ebbe8c2add 100644 --- a/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlCodecFactory.java +++ b/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlCodecFactory.java @@ -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> { - 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> { * @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> { * @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> { @Override protected XmlCodec identityRefCodec(final IdentityrefTypeDefinition type, final QNameModule module) { - return new IdentityrefXmlCodec(getEffectiveModelContext(), pref, module); + return new IdentityrefXmlCodec(getEffectiveModelContext(), module, pref); } @Override diff --git a/codec/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/NamespacePrefixesTest.java b/codec/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/NamespacePrefixesTest.java index d6fb285355..69980a97ec 100644 --- a/codec/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/NamespacePrefixesTest.java +++ b/codec/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/NamespacePrefixesTest.java @@ -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(); 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"); diff --git a/codec/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/PreferredPrefixesTest.java b/codec/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/PreferredPrefixesTest.java index db4c9b710b..f6ec9c92e7 100644 --- a/codec/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/PreferredPrefixesTest.java +++ b/codec/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/PreferredPrefixesTest.java @@ -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()); + } } diff --git a/codec/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlStreamUtilsTest.java b/codec/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlStreamUtilsTest.java index 2c51263597..c01e948b5f 100644 --- a/codec/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlStreamUtilsTest.java +++ b/codec/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlStreamUtilsTest.java @@ -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(); diff --git a/codec/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/YT1473Test.java b/codec/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/YT1473Test.java index 5c1aa55c10..44944e85e7 100644 --- a/codec/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/YT1473Test.java +++ b/codec/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/YT1473Test.java @@ -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 diff --git a/codec/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/YT1543Test.java b/codec/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/YT1543Test.java index 4cd307d01e..bba8c3ccf2 100644 --- a/codec/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/YT1543Test.java +++ b/codec/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/YT1543Test.java @@ -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();