From e4d7042ceaaf302725209f5bdb037be99a092790 Mon Sep 17 00:00:00 2001 From: Robert Varga Date: Fri, 21 Jul 2023 01:35:57 +0200 Subject: [PATCH] Do not trim element content for string leaf values Trimming element text leads to values being mis-represented -- for example if the content is a YANG string, we really need to pick it up directly. We should completely avoid trimming, but that would lead to not recognizing previously-accepted XML documents, for example those which correspond to a uint8 with leading whitespace. JIRA: YANGTOOLS-1522 Change-Id: Ib1e05a9428a66765b2f85cfde723fe27076f68ee Signed-off-by: Robert Varga --- .../yang/data/codec/xml/AbstractXmlCodec.java | 13 ++++-- .../data/codec/xml/IdentityrefXmlCodec.java | 24 +++++----- .../yang/data/codec/xml/QuotedXmlCodec.java | 6 +-- .../yang/data/codec/xml/StringXmlCodec.java | 24 ++++++++++ .../yang/data/codec/xml/XmlCodecFactory.java | 3 +- .../yang/data/codec/xml/XmlParserStream.java | 2 +- .../xml/XmlStringInstanceIdentifierCodec.java | 23 +++++----- .../yang/data/codec/xml/YT1522Test.java | 44 +++++++++++++++++++ 8 files changed, 105 insertions(+), 34 deletions(-) create mode 100644 codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/StringXmlCodec.java create mode 100644 codec/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/YT1522Test.java diff --git a/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/AbstractXmlCodec.java b/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/AbstractXmlCodec.java index 942e4dbc0a..8cc15779a0 100644 --- a/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/AbstractXmlCodec.java +++ b/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/AbstractXmlCodec.java @@ -5,12 +5,12 @@ * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ - package org.opendaylight.yangtools.yang.data.codec.xml; import static java.util.Objects.requireNonNull; import javax.xml.namespace.NamespaceContext; +import org.eclipse.jdt.annotation.NonNull; import org.opendaylight.yangtools.yang.data.impl.codec.DataStringCodec; import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec; @@ -20,10 +20,9 @@ import org.opendaylight.yangtools.yang.data.impl.codec.TypeDefinitionAwareCodec; * @param Deserialized object type */ abstract class AbstractXmlCodec implements XmlCodec { - private final DataStringCodec codec; - protected AbstractXmlCodec(final DataStringCodec codec) { + AbstractXmlCodec(final DataStringCodec codec) { this.codec = requireNonNull(codec); } @@ -34,7 +33,13 @@ abstract class AbstractXmlCodec implements XmlCodec { @Override public final T parseValue(final NamespaceContext namespaceContext, final String str) { - return codec.deserialize(str); + return codec.deserialize(trimValue(str)); + } + + // FIXME: YANGTOOLS-1523: remove this method + @Deprecated + @NonNull String trimValue(final @NonNull String str) { + return str.trim(); } final String serialize(final T input) { 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 9f9980e8f1..cb931bf53e 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 @@ -11,25 +11,23 @@ import static com.google.common.base.Preconditions.checkArgument; import static java.util.Objects.requireNonNull; import com.google.common.base.MoreObjects; -import java.util.Iterator; -import java.util.Map.Entry; import javax.xml.namespace.NamespaceContext; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamWriter; +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.XMLNamespace; import org.opendaylight.yangtools.yang.data.util.codec.IdentityCodecUtil; import org.opendaylight.yangtools.yang.data.util.codec.QNameCodecUtil; import org.opendaylight.yangtools.yang.model.api.EffectiveModelContext; -import org.opendaylight.yangtools.yang.model.api.Module; final class IdentityrefXmlCodec implements XmlCodec { - private final EffectiveModelContext schemaContext; - private final QNameModule parentModule; + private final @NonNull EffectiveModelContext context; + private final @NonNull QNameModule parentModule; IdentityrefXmlCodec(final EffectiveModelContext context, final QNameModule parentModule) { - schemaContext = requireNonNull(context); + this.context = requireNonNull(context); this.parentModule = requireNonNull(parentModule); } @@ -40,16 +38,16 @@ final class IdentityrefXmlCodec implements XmlCodec { @Override public QName parseValue(final NamespaceContext ctx, final String str) { - return IdentityCodecUtil.parseIdentity(str, schemaContext, prefix -> { + // FIXME: YANGTOOLS-1523: do not trim() + return IdentityCodecUtil.parseIdentity(str.trim(), context, prefix -> { if (prefix.isEmpty()) { return parentModule; } - final String prefixedNS = ctx.getNamespaceURI(prefix); + final var prefixedNS = ctx.getNamespaceURI(prefix); checkArgument(prefixedNS != null, "Failed to resolve prefix %s", prefix); - final Iterator modules = - schemaContext.findModules(XMLNamespace.of(prefixedNS)).iterator(); + final var modules = context.findModules(XMLNamespace.of(prefixedNS)).iterator(); checkArgument(modules.hasNext(), "Could not find module for namespace %s", prefixedNS); return modules.next().getQNameModule(); }).getQName(); @@ -57,10 +55,10 @@ final class IdentityrefXmlCodec implements XmlCodec { @Override public void writeValue(final XMLStreamWriter ctx, final QName value) throws XMLStreamException { - final RandomPrefix prefixes = new RandomPrefix(ctx.getNamespaceContext()); - final String str = QNameCodecUtil.encodeQName(value, uri -> prefixes.encodePrefix(uri.getNamespace())); + final var prefixes = new RandomPrefix(ctx.getNamespaceContext()); + final var str = QNameCodecUtil.encodeQName(value, uri -> prefixes.encodePrefix(uri.getNamespace())); - for (Entry e : prefixes.getPrefixes()) { + for (var e : prefixes.getPrefixes()) { ctx.writeNamespace(e.getValue(), e.getKey().toString()); } ctx.writeCharacters(str); diff --git a/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/QuotedXmlCodec.java b/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/QuotedXmlCodec.java index 720728bb4f..ca2bb35e4d 100644 --- a/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/QuotedXmlCodec.java +++ b/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/QuotedXmlCodec.java @@ -5,20 +5,20 @@ * terms of the Eclipse Public License v1.0 which accompanies this distribution, * and is available at http://www.eclipse.org/legal/epl-v10.html */ - package org.opendaylight.yangtools.yang.data.codec.xml; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamWriter; import org.opendaylight.yangtools.yang.data.impl.codec.DataStringCodec; -final class QuotedXmlCodec extends AbstractXmlCodec { +// FIXME: YANGTOOLS-1523: make this class final +class QuotedXmlCodec extends AbstractXmlCodec { QuotedXmlCodec(final DataStringCodec codec) { super(codec); } @Override - public void writeValue(final XMLStreamWriter ctx, final T value) throws XMLStreamException { + public final void writeValue(final XMLStreamWriter ctx, final T value) throws XMLStreamException { ctx.writeCharacters(serialize(value)); } } diff --git a/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/StringXmlCodec.java b/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/StringXmlCodec.java new file mode 100644 index 0000000000..a38a76ba18 --- /dev/null +++ b/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/StringXmlCodec.java @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2023 PANTHEON.tech, 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.data.codec.xml; + +import org.opendaylight.yangtools.yang.data.impl.codec.StringStringCodec; + +// FIXME: YANGTOOLS-1523: remove this class +@Deprecated +final class StringXmlCodec extends QuotedXmlCodec { + StringXmlCodec(final StringStringCodec codec) { + super(codec); + } + + @Override + @Deprecated + String trimValue(final String str) { + return str; + } +} 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 920b7db857..fbe3fe86ea 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 @@ -142,7 +142,8 @@ public final class XmlCodecFactory extends AbstractCodecFactory> { @Override protected XmlCodec stringCodec(final StringTypeDefinition type) { - return new QuotedXmlCodec<>(StringStringCodec.from(type)); + // FIXME: YANGTOOLS-1523: use QuotedXmlCodec + return new StringXmlCodec(StringStringCodec.from(type)); } @Override diff --git a/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlParserStream.java b/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlParserStream.java index 24c704e13e..fbbfdff4eb 100644 --- a/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlParserStream.java +++ b/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlParserStream.java @@ -431,7 +431,7 @@ public final class XmlParserStream implements Closeable, Flushable { if (parent instanceof LeafNodeDataWithSchema || parent instanceof LeafListEntryNodeDataWithSchema) { parent.setAttributes(getElementAttributes(in)); - setValue((SimpleNodeDataWithSchema) parent, in.getElementText().trim(), in.getNamespaceContext()); + setValue((SimpleNodeDataWithSchema) parent, in.getElementText(), in.getNamespaceContext()); if (isNextEndDocument(in)) { return; } diff --git a/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlStringInstanceIdentifierCodec.java b/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlStringInstanceIdentifierCodec.java index 1b24568963..3e592e551b 100644 --- a/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlStringInstanceIdentifierCodec.java +++ b/codec/yang-data-codec-xml/src/main/java/org/opendaylight/yangtools/yang/data/codec/xml/XmlStringInstanceIdentifierCodec.java @@ -11,7 +11,6 @@ import static java.util.Objects.requireNonNull; import java.util.ArrayDeque; import java.util.Deque; -import java.util.Iterator; import javax.xml.namespace.NamespaceContext; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamWriter; @@ -36,22 +35,22 @@ final class XmlStringInstanceIdentifierCodec extends AbstractModuleStringInstanc private final @NonNull XmlCodecFactory codecFactory; private final @NonNull EffectiveModelContext context; - XmlStringInstanceIdentifierCodec(final EffectiveModelContext context, final XmlCodecFactory xmlCodecFactory) { + XmlStringInstanceIdentifierCodec(final EffectiveModelContext context, final XmlCodecFactory codecFactory) { this.context = requireNonNull(context); - this.dataContextTree = DataSchemaContextTree.from(context); - this.codecFactory = requireNonNull(xmlCodecFactory); + this.codecFactory = requireNonNull(codecFactory); + dataContextTree = DataSchemaContextTree.from(context); } @Override protected Module moduleForPrefix(final String prefix) { - final String prefixedNS = getNamespaceContext().getNamespaceURI(prefix); - final Iterator modules = context.findModules(XMLNamespace.of(prefixedNS)).iterator(); + final var prefixedNS = getNamespaceContext().getNamespaceURI(prefix); + final var modules = context.findModules(XMLNamespace.of(prefixedNS)).iterator(); return modules.hasNext() ? modules.next() : null; } @Override protected String prefixForNamespace(final XMLNamespace namespace) { - final Iterator modules = context.findModules(namespace).iterator(); + final var modules = context.findModules(namespace).iterator(); return modules.hasNext() ? modules.next().getName() : null; } @@ -85,15 +84,15 @@ final class XmlStringInstanceIdentifierCodec extends AbstractModuleStringInstanc public YangInstanceIdentifier parseValue(final NamespaceContext ctx, final String str) { pushNamespaceContext(ctx); try { - return deserialize(str); + // FIXME: YANGTOOLS-1523: do not trim() + return deserialize(str.trim()); } finally { popNamespaceContext(); } } @Override - public void writeValue(final XMLStreamWriter ctx, final YangInstanceIdentifier value) - throws XMLStreamException { + public void writeValue(final XMLStreamWriter ctx, final YangInstanceIdentifier value) throws XMLStreamException { ctx.writeCharacters(serialize(value)); } @@ -102,7 +101,7 @@ final class XmlStringInstanceIdentifierCodec extends AbstractModuleStringInstanc } private static void popNamespaceContext() { - final Deque stack = TL_CONTEXT.get(); + final var stack = TL_CONTEXT.get(); stack.pop(); if (stack.isEmpty()) { TL_CONTEXT.set(null); @@ -110,7 +109,7 @@ final class XmlStringInstanceIdentifierCodec extends AbstractModuleStringInstanc } private static void pushNamespaceContext(final NamespaceContext context) { - Deque stack = TL_CONTEXT.get(); + var stack = TL_CONTEXT.get(); if (stack == null) { stack = new ArrayDeque<>(1); TL_CONTEXT.set(stack); diff --git a/codec/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/YT1522Test.java b/codec/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/YT1522Test.java new file mode 100644 index 0000000000..d764bb4660 --- /dev/null +++ b/codec/yang-data-codec-xml/src/test/java/org/opendaylight/yangtools/yang/data/codec/xml/YT1522Test.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2023 PANTHEON.tech, 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.data.codec.xml; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import java.io.StringReader; +import org.junit.jupiter.api.Test; +import org.opendaylight.yangtools.util.xml.UntrustedXML; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes; +import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNormalizedNodeStreamWriter; +import org.opendaylight.yangtools.yang.data.impl.schema.NormalizationResultHolder; +import org.opendaylight.yangtools.yang.model.util.SchemaInferenceStack.Inference; +import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils; + +class YT1522Test { + @Test + void testWhitespaceLeaf() throws Exception { + final var context = YangParserTestUtils.parseYang(""" + module foo { + namespace foo; + prefix foo; + + leaf foo { + type string { + length 1..4; + } + } + }"""); + final var result = new NormalizationResultHolder(); + final var streamWriter = ImmutableNormalizedNodeStreamWriter.from(result); + final var xmlParser = XmlParserStream.create(streamWriter, + Inference.ofDataTreePath(context, QName.create("foo", "foo"))); + xmlParser.parse(UntrustedXML.createXMLStreamReader(new StringReader(""" + """))); + assertEquals(ImmutableNodes.leafNode(QName.create("foo", "foo"), " "), result.getResult().data()); + } +} -- 2.36.6