From 8a55083db823d07a808ba61c1b2c2f19f684d4c1 Mon Sep 17 00:00:00 2001 From: Peter Kajsa Date: Tue, 22 Mar 2016 18:39:20 +0100 Subject: [PATCH] Bug 5396: Regex processing of yang models is broken. Yang statement parser should not replace type statement context by potentional type statement context from the Map of context's substatements. Change-Id: I9fa90f53bf8d7f9ebb269628abadeab1bf4c03b5 Signed-off-by: Peter Kajsa --- .../transform/dom/serializer/Bug5396.java | 130 ++++++++++++++++++ .../src/test/resources/bug5396/xml/foo.xml | 6 + .../src/test/resources/bug5396/xml/foo2.xml | 6 + .../src/test/resources/bug5396/xml/foo3.xml | 6 + .../src/test/resources/bug5396/xml/foo4.xml | 6 + .../src/test/resources/bug5396/xml/foo5.xml | 6 + .../resources/bug5396/xml/invalid-foo.xml | 6 + .../src/test/resources/bug5396/yang/foo.yang | 32 +++++ .../stmt/reactor/StatementContextBase.java | 3 +- .../yangtools/yang/stmt/test/Bug5396.java | 94 +++++++++++++ .../src/test/resources/bugs/bug5396/foo.yang | 28 ++++ 11 files changed, 322 insertions(+), 1 deletion(-) create mode 100644 yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/transform/dom/serializer/Bug5396.java create mode 100644 yang/yang-data-impl/src/test/resources/bug5396/xml/foo.xml create mode 100644 yang/yang-data-impl/src/test/resources/bug5396/xml/foo2.xml create mode 100644 yang/yang-data-impl/src/test/resources/bug5396/xml/foo3.xml create mode 100644 yang/yang-data-impl/src/test/resources/bug5396/xml/foo4.xml create mode 100644 yang/yang-data-impl/src/test/resources/bug5396/xml/foo5.xml create mode 100644 yang/yang-data-impl/src/test/resources/bug5396/xml/invalid-foo.xml create mode 100644 yang/yang-data-impl/src/test/resources/bug5396/yang/foo.yang create mode 100644 yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/stmt/test/Bug5396.java create mode 100644 yang/yang-parser-impl/src/test/resources/bugs/bug5396/foo.yang diff --git a/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/transform/dom/serializer/Bug5396.java b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/transform/dom/serializer/Bug5396.java new file mode 100644 index 0000000000..b7ba1f3b75 --- /dev/null +++ b/yang/yang-data-impl/src/test/java/org/opendaylight/yangtools/yang/data/impl/schema/transform/dom/serializer/Bug5396.java @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.serializer; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import com.google.common.base.Optional; +import com.google.common.base.Preconditions; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.URI; +import java.util.Collections; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.stream.XMLOutputFactory; +import org.junit.Before; +import org.junit.Test; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.QNameModule; +import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.NodeIdentifier; +import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.PathArgument; +import org.opendaylight.yangtools.yang.data.api.schema.ContainerNode; +import org.opendaylight.yangtools.yang.data.api.schema.DataContainerChild; +import org.opendaylight.yangtools.yang.data.api.schema.LeafNode; +import org.opendaylight.yangtools.yang.data.impl.RetestUtils; +import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.DomUtils; +import org.opendaylight.yangtools.yang.data.impl.schema.transform.dom.parser.DomToNormalizedNodeParserFactory; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.w3c.dom.Document; +import org.xml.sax.SAXException; + +public class Bug5396 { + private static final XMLOutputFactory XML_FACTORY; + private static final DocumentBuilderFactory BUILDERFACTORY; + + static { + XML_FACTORY = XMLOutputFactory.newFactory(); + XML_FACTORY.setProperty(XMLOutputFactory.IS_REPAIRING_NAMESPACES, false); + + BUILDERFACTORY = DocumentBuilderFactory.newInstance(); + BUILDERFACTORY.setNamespaceAware(true); + BUILDERFACTORY.setCoalescing(true); + BUILDERFACTORY.setIgnoringElementContentWhitespace(true); + BUILDERFACTORY.setIgnoringComments(true); + } + + private QNameModule fooModuleQName; + private QName root; + private SchemaContext schemaContext; + + @Before + public void Init() throws Exception { + fooModuleQName = QNameModule.create(new URI("foo"), SimpleDateFormatUtil.getRevisionFormat() + .parse("2016-03-22")); + root = QName.create(fooModuleQName, "root"); + schemaContext = RetestUtils + .parseYangSources(new File(getClass().getResource("/bug5396/yang/foo.yang").toURI())); + } + + @Test + public void test() throws Exception { + testInputXML("/bug5396/xml/foo.xml", "dp1o34"); + testInputXML("/bug5396/xml/foo2.xml", "dp0s3f9"); + testInputXML("/bug5396/xml/foo3.xml", "dp09P1p2s3"); + testInputXML("/bug5396/xml/foo4.xml", "dp0p3p1"); + testInputXML("/bug5396/xml/foo5.xml", "dp0s3"); + + try { + testInputXML("/bug5396/xml/invalid-foo.xml", null); + fail("Test should fail due to invalid input string"); + } catch (IllegalArgumentException e) { + assertTrue(e + .getMessage() + .startsWith( + "Failed to parse element [my-leaf: null] as leaf AbsoluteSchemaPath{path=[(foo?revision=2016-03-22)root, (foo?revision=2016-03-22)my-leaf]")); + } + } + + public void testInputXML(String xmlPath, String expectedValue) throws Exception { + final Document doc = loadDocument(xmlPath); + + final ContainerNode output = DomToNormalizedNodeParserFactory + .getInstance(DomUtils.defaultValueCodecProvider(), schemaContext).getContainerNodeParser() + .parse(Collections.singletonList(doc.getDocumentElement()), schemaContext); + + assertNotNull(output); + + Optional> child = output.getChild(new NodeIdentifier(root)); + assertTrue(child.orNull() instanceof ContainerNode); + ContainerNode rootContainer = (ContainerNode) child.get(); + + Optional> myLeaf = rootContainer.getChild(new NodeIdentifier( + QName.create(fooModuleQName, "my-leaf"))); + assertTrue(myLeaf.orNull() instanceof LeafNode); + + assertEquals(expectedValue, myLeaf.get().getValue()); + } + + private static Document loadDocument(final String xmlPath) throws IOException, SAXException { + final InputStream resourceAsStream = Bug5396.class.getResourceAsStream(xmlPath); + + final Document currentConfigElement = readXmlToDocument(resourceAsStream); + Preconditions.checkNotNull(currentConfigElement); + return currentConfigElement; + } + + private static Document readXmlToDocument(final InputStream xmlContent) throws IOException, SAXException { + final DocumentBuilder dBuilder; + try { + dBuilder = BUILDERFACTORY.newDocumentBuilder(); + } catch (final ParserConfigurationException e) { + throw new RuntimeException("Failed to parse XML document", e); + } + final Document doc = dBuilder.parse(xmlContent); + + doc.getDocumentElement().normalize(); + return doc; + } +} diff --git a/yang/yang-data-impl/src/test/resources/bug5396/xml/foo.xml b/yang/yang-data-impl/src/test/resources/bug5396/xml/foo.xml new file mode 100644 index 0000000000..26fe22204b --- /dev/null +++ b/yang/yang-data-impl/src/test/resources/bug5396/xml/foo.xml @@ -0,0 +1,6 @@ + + + + dp1o34 + + \ No newline at end of file diff --git a/yang/yang-data-impl/src/test/resources/bug5396/xml/foo2.xml b/yang/yang-data-impl/src/test/resources/bug5396/xml/foo2.xml new file mode 100644 index 0000000000..72b9185fee --- /dev/null +++ b/yang/yang-data-impl/src/test/resources/bug5396/xml/foo2.xml @@ -0,0 +1,6 @@ + + + + dp0s3f9 + + \ No newline at end of file diff --git a/yang/yang-data-impl/src/test/resources/bug5396/xml/foo3.xml b/yang/yang-data-impl/src/test/resources/bug5396/xml/foo3.xml new file mode 100644 index 0000000000..c18e3cc30b --- /dev/null +++ b/yang/yang-data-impl/src/test/resources/bug5396/xml/foo3.xml @@ -0,0 +1,6 @@ + + + + dp09P1p2s3 + + \ No newline at end of file diff --git a/yang/yang-data-impl/src/test/resources/bug5396/xml/foo4.xml b/yang/yang-data-impl/src/test/resources/bug5396/xml/foo4.xml new file mode 100644 index 0000000000..154a11489b --- /dev/null +++ b/yang/yang-data-impl/src/test/resources/bug5396/xml/foo4.xml @@ -0,0 +1,6 @@ + + + + dp0p3p1 + + \ No newline at end of file diff --git a/yang/yang-data-impl/src/test/resources/bug5396/xml/foo5.xml b/yang/yang-data-impl/src/test/resources/bug5396/xml/foo5.xml new file mode 100644 index 0000000000..7a5ce58cd3 --- /dev/null +++ b/yang/yang-data-impl/src/test/resources/bug5396/xml/foo5.xml @@ -0,0 +1,6 @@ + + + + dp0s3 + + \ No newline at end of file diff --git a/yang/yang-data-impl/src/test/resources/bug5396/xml/invalid-foo.xml b/yang/yang-data-impl/src/test/resources/bug5396/xml/invalid-foo.xml new file mode 100644 index 0000000000..cf86806066 --- /dev/null +++ b/yang/yang-data-impl/src/test/resources/bug5396/xml/invalid-foo.xml @@ -0,0 +1,6 @@ + + + + dp09P1p2s1234 + + \ No newline at end of file diff --git a/yang/yang-data-impl/src/test/resources/bug5396/yang/foo.yang b/yang/yang-data-impl/src/test/resources/bug5396/yang/foo.yang new file mode 100644 index 0000000000..bad4e39e6b --- /dev/null +++ b/yang/yang-data-impl/src/test/resources/bug5396/yang/foo.yang @@ -0,0 +1,32 @@ +module foo { + yang-version 1; + namespace "foo"; + prefix "foo"; + + revision "2016-03-22" { + description "test"; + } + + container root { + leaf my-leaf { + type my-type; + } + } + + typedef my-type { + type union { + type string { + pattern "dp[0-9]+o[0-9]+"; + } + type string { + pattern "dp[0-9]+s[0-9]+(f[0-9]+)?(d[0-9]+)?"; + } + type string { + pattern "dp[0-9]+(P[0-9]+)?p[0-9]{1,3}s[0-9]{1,3}(f[0-9]+)?(d[0-9]+)?"; + } + type string { + pattern "dp[0-9]+p[0-9]+p[0-9]+"; + } + } + } +} diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/StatementContextBase.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/StatementContextBase.java index 92b2d0bd37..21c0b18b5b 100644 --- a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/StatementContextBase.java +++ b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/StatementContextBase.java @@ -319,7 +319,8 @@ public abstract class StatementContextBase, E StatementContextBase potential = null; StatementDefinition stmtDef = getDefinition().getPublicView(); - if (stmtDef != Rfc6020Mapping.AUGMENT && stmtDef != Rfc6020Mapping.DEVIATION) { + if (stmtDef != Rfc6020Mapping.AUGMENT && stmtDef != Rfc6020Mapping.DEVIATION + && stmtDef != Rfc6020Mapping.TYPE) { potential = substatements.get(createIdentifier()); } if (potential == null) { diff --git a/yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/stmt/test/Bug5396.java b/yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/stmt/test/Bug5396.java new file mode 100644 index 0000000000..932339604c --- /dev/null +++ b/yang/yang-parser-impl/src/test/java/org/opendaylight/yangtools/yang/stmt/test/Bug5396.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2016 Cisco Systems, Inc. and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ +package org.opendaylight.yangtools.yang.stmt.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.io.FileNotFoundException; +import java.net.URISyntaxException; +import java.util.List; +import org.junit.Test; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.api.SchemaNode; +import org.opendaylight.yangtools.yang.model.api.SchemaPath; +import org.opendaylight.yangtools.yang.model.api.TypeDefinition; +import org.opendaylight.yangtools.yang.model.api.type.PatternConstraint; +import org.opendaylight.yangtools.yang.model.api.type.StringTypeDefinition; +import org.opendaylight.yangtools.yang.model.api.type.UnionTypeDefinition; +import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil; +import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException; +import org.opendaylight.yangtools.yang.parser.spi.source.SourceException; + +public class Bug5396 { + @Test + public void test() throws SourceException, FileNotFoundException, ReactorException, URISyntaxException { + SchemaContext context = StmtTestUtils.parseYangSources("/bugs/bug5396"); + assertNotNull(context); + + QName root = QName.create("foo", "1970-01-01", "root"); + QName myLeaf2 = QName.create("foo", "1970-01-01", "my-leaf2"); + + SchemaPath schemaPath = SchemaPath.create(true, root, myLeaf2); + SchemaNode findDataSchemaNode = SchemaContextUtil.findDataSchemaNode(context, schemaPath); + assertTrue(findDataSchemaNode instanceof LeafSchemaNode); + + LeafSchemaNode leaf2 = (LeafSchemaNode) findDataSchemaNode; + TypeDefinition type = leaf2.getType(); + assertTrue(type instanceof UnionTypeDefinition); + + UnionTypeDefinition union = (UnionTypeDefinition) type; + List> types = union.getTypes(); + + assertEquals(4, types.size()); + + TypeDefinition type0 = types.get(0); + TypeDefinition type1 = types.get(1); + TypeDefinition type2 = types.get(2); + TypeDefinition type3 = types.get(3); + + assertFalse(type0.equals(type1)); + assertFalse(type0.equals(type2)); + assertFalse(type0.equals(type3)); + + assertTrue(type0 instanceof StringTypeDefinition); + assertTrue(type1 instanceof StringTypeDefinition); + assertTrue(type2 instanceof StringTypeDefinition); + assertTrue(type3 instanceof StringTypeDefinition); + + StringTypeDefinition stringType0 = (StringTypeDefinition) type0; + StringTypeDefinition stringType1 = (StringTypeDefinition) type1; + StringTypeDefinition stringType2 = (StringTypeDefinition) type2; + StringTypeDefinition stringType3 = (StringTypeDefinition) type3; + + List patternConstraints0 = stringType0.getPatternConstraints(); + List patternConstraints1 = stringType1.getPatternConstraints(); + List patternConstraints2 = stringType2.getPatternConstraints(); + List patternConstraints3 = stringType3.getPatternConstraints(); + + assertEquals(1, patternConstraints0.size()); + assertEquals(1, patternConstraints1.size()); + assertEquals(1, patternConstraints2.size()); + assertEquals(1, patternConstraints3.size()); + + PatternConstraint patternConstraint0 = patternConstraints0.get(0); + PatternConstraint patternConstraint1 = patternConstraints1.get(0); + PatternConstraint patternConstraint2 = patternConstraints2.get(0); + PatternConstraint patternConstraint3 = patternConstraints3.get(0); + + assertEquals("^dp[0-9]+o[0-9]+(d[0-9]+)?$", patternConstraint0.getRegularExpression()); + assertEquals("^dp[0-9]+s[0-9]+(f[0-9]+)?(d[0-9]+)?$", patternConstraint1.getRegularExpression()); + assertEquals("^dp[0-9]+(P[0-9]+)?p[0-9]{1,3}s[0-9]{1,3}(f[0-9]+)?(d[0-9]+)?$", + patternConstraint2.getRegularExpression()); + assertEquals("^dp[0-9]+p[0-9]+p[0-9]+$", patternConstraint3.getRegularExpression()); + } +} diff --git a/yang/yang-parser-impl/src/test/resources/bugs/bug5396/foo.yang b/yang/yang-parser-impl/src/test/resources/bugs/bug5396/foo.yang new file mode 100644 index 0000000000..ca419eee4c --- /dev/null +++ b/yang/yang-parser-impl/src/test/resources/bugs/bug5396/foo.yang @@ -0,0 +1,28 @@ +module foo { + yang-version 1; + namespace "foo"; + prefix "foo"; + + container root { + leaf my-leaf2 { + type my-type; + } + } + + typedef my-type { + type union { + type string { + pattern "dp[0-9]+o[0-9]+(d[0-9]+)?"; + } + type string { + pattern "dp[0-9]+s[0-9]+(f[0-9]+)?(d[0-9]+)?"; + } + type string { + pattern "dp[0-9]+(P[0-9]+)?p[0-9]{1,3}s[0-9]{1,3}(f[0-9]+)?(d[0-9]+)?"; + } + type string { + pattern "dp[0-9]+p[0-9]+p[0-9]+"; + } + } + } +} -- 2.36.6