import com.google.common.base.Optional;
import com.google.common.base.Throwables;
import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
import java.io.IOException;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.util.Collections;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
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.DataContainerNode;
import org.opendaylight.yangtools.yang.data.api.schema.NormalizedNode;
import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeStreamWriter;
import org.opendaylight.yangtools.yang.data.api.schema.stream.NormalizedNodeWriter;
import org.opendaylight.yangtools.yang.data.impl.codec.xml.XMLStreamNormalizedNodeStreamWriter;
import org.opendaylight.yangtools.yang.data.impl.schema.ImmutableNodes;
-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.ContainerSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
-import org.opendaylight.yangtools.yang.model.api.Module;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.opendaylight.yangtools.yang.model.api.SchemaPath;
import org.slf4j.Logger;
protected static final String FILTER = "filter";
static final YangInstanceIdentifier ROOT = YangInstanceIdentifier.builder().build();
protected final CurrentSchemaContext schemaContext;
+ private final FilterContentValidator validator;
public AbstractGet(final String netconfSessionIdForReporting, final CurrentSchemaContext schemaContext) {
super(netconfSessionIdForReporting);
this.schemaContext = schemaContext;
+ this.validator = new FilterContentValidator(schemaContext);
}
private static final XMLOutputFactory XML_OUTPUT_FACTORY;
}
}
- private DataSchemaNode getSchemaNodeFromNamespace(final XmlElement element) throws DocumentedException {
-
- try {
- final Module module = schemaContext.getCurrentContext().findModuleByNamespaceAndRevision(new URI(element.getNamespace()), null);
- DataSchemaNode dataSchemaNode = module.getDataChildByName(element.getName());
- if (dataSchemaNode != null) {
- return dataSchemaNode;
- }
- } catch (URISyntaxException e) {
- LOG.debug("Error during parsing of element namespace, this should not happen since namespace of an xml " +
- "element is valid and if the xml was parsed then the URI should be as well");
- throw new IllegalArgumentException("Unable to parse element namespace, this should not happen since " +
- "namespace of an xml element is valid and if the xml was parsed then the URI should be as well");
- }
- throw new DocumentedException("Unable to find node with namespace: " + element.getNamespace() + "in schema context: " + schemaContext.getCurrentContext().toString(),
- ErrorType.application,
- ErrorTag.unknown_namespace,
- ErrorSeverity.error);
- }
-
protected Element serializeNodeWithParentStructure(Document document, YangInstanceIdentifier dataRoot, NormalizedNode node) {
if (!dataRoot.equals(ROOT)) {
return (Element) transformNormalizedNode(document,
}
XmlElement element = filterElement.getOnlyChildElement();
- DataSchemaNode schemaNode = getSchemaNodeFromNamespace(element);
-
- return getReadPointFromNode(YangInstanceIdentifier.builder().build(), filterToNormalizedNode(element, schemaNode));
- }
-
- private YangInstanceIdentifier getReadPointFromNode(final YangInstanceIdentifier pathArg, final NormalizedNode nNode) {
- final YangInstanceIdentifier path = pathArg.node(nNode.getIdentifier());
- if (nNode instanceof DataContainerNode) {
- DataContainerNode node = (DataContainerNode) nNode;
- if (node.getValue().size() == 1) {
- return getReadPointFromNode(path, (NormalizedNode) Lists.newArrayList(node.getValue()).get(0));
- }
- }
- return path;
- }
-
- private NormalizedNode filterToNormalizedNode(XmlElement element, DataSchemaNode schemaNode) throws DocumentedException {
- DomToNormalizedNodeParserFactory parserFactory = DomToNormalizedNodeParserFactory
- .getInstance(DomUtils.defaultValueCodecProvider(), schemaContext.getCurrentContext());
-
- final NormalizedNode parsedNode;
-
- if (schemaNode instanceof ContainerSchemaNode) {
- parsedNode = parserFactory.getContainerNodeParser().parse(Collections.singletonList(element.getDomElement()), (ContainerSchemaNode) schemaNode);
- } else if (schemaNode instanceof ListSchemaNode) {
- parsedNode = parserFactory.getMapNodeParser().parse(Collections.singletonList(element.getDomElement()), (ListSchemaNode) schemaNode);
- } else {
- throw new DocumentedException("Schema node of the top level element is not an instance of container or list",
- ErrorType.application, ErrorTag.unknown_element, ErrorSeverity.error);
- }
- return parsedNode;
+ return validator.validate(element);
}
protected static final class GetConfigExecution {
--- /dev/null
+/*
+ * 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.netconf.mdsal.connector.ops.get;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Deque;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.opendaylight.controller.config.util.xml.DocumentedException;
+import org.opendaylight.controller.config.util.xml.MissingNameSpaceException;
+import org.opendaylight.controller.config.util.xml.XmlElement;
+import org.opendaylight.netconf.mdsal.connector.CurrentSchemaContext;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
+import org.opendaylight.yangtools.yang.model.api.ChoiceCaseNode;
+import org.opendaylight.yangtools.yang.model.api.ChoiceSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
+
+/**
+ * Class validates filter content against schema context.
+ */
+public class FilterContentValidator {
+
+ private final CurrentSchemaContext schemaContext;
+
+ /**
+ * @param schemaContext current schema context
+ */
+ public FilterContentValidator(CurrentSchemaContext schemaContext) {
+ this.schemaContext = schemaContext;
+ }
+
+ /**
+ * Validates filter content against this validator schema context. If the filter is valid, method returns {@link YangInstanceIdentifier}
+ * of node which can be used as root for data selection.
+ * @param filterContent filter content
+ * @return YangInstanceIdentifier
+ * @throws DocumentedException if filter content is not valid
+ */
+ public YangInstanceIdentifier validate(XmlElement filterContent) throws DocumentedException {
+ try {
+ final URI namespace = new URI(filterContent.getNamespace());
+ final Module module = schemaContext.getCurrentContext().findModuleByNamespaceAndRevision(namespace, null);
+ final DataSchemaNode schema = getRootDataSchemaNode(module, namespace, filterContent.getName());
+ final FilterTree filterTree = validateNode(filterContent, schema, new FilterTree(schema.getQName(), Type.OTHER));
+ return getFilterDataRoot(filterTree, YangInstanceIdentifier.builder());
+ } catch (DocumentedException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new DocumentedException("Validation failed. Cause: " + e.getMessage(),
+ DocumentedException.ErrorType.application,
+ DocumentedException.ErrorTag.unknown_namespace,
+ DocumentedException.ErrorSeverity.error);
+ }
+ }
+
+ /**
+ * Returns module's child data node of given name space and name
+ * @param module module
+ * @param nameSpace name space
+ * @param name name
+ * @return child data node schema
+ * @throws DocumentedException if child with given name is not present
+ */
+ private DataSchemaNode getRootDataSchemaNode(Module module, URI nameSpace, String name) throws DocumentedException {
+ final Collection<DataSchemaNode> childNodes = module.getChildNodes();
+ for (DataSchemaNode childNode : childNodes) {
+ final QName qName = childNode.getQName();
+ if (qName.getNamespace().equals(nameSpace) && qName.getLocalName().equals(name)) {
+ return childNode;
+ }
+ }
+ throw new DocumentedException("Unable to find node with namespace: " + nameSpace + "in schema context: " + schemaContext.getCurrentContext().toString(),
+ DocumentedException.ErrorType.application,
+ DocumentedException.ErrorTag.unknown_namespace,
+ DocumentedException.ErrorSeverity.error);
+ }
+
+ /**
+ * Recursively checks filter elements against the schema. Returns tree of nodes QNames as they appear in filter.
+ * @param element element to check
+ * @param parentNodeSchema parent node schema
+ * @param tree parent node tree
+ * @return tree
+ * @throws ValidationException if filter content is not valid
+ */
+ private FilterTree validateNode(XmlElement element, DataSchemaNode parentNodeSchema, FilterTree tree) throws ValidationException {
+ final List<XmlElement> childElements = element.getChildElements();
+ for (XmlElement childElement : childElements) {
+ try {
+ final Deque<DataSchemaNode> path = findSchemaNodeByNameAndNamespace(parentNodeSchema, childElement.getName(), new URI(childElement.getNamespace()));
+ if (path.isEmpty()) {
+ throw new ValidationException(element, childElement);
+ }
+ FilterTree subtree = tree;
+ for (DataSchemaNode dataSchemaNode : path) {
+ subtree = subtree.addChild(dataSchemaNode);
+ }
+ final DataSchemaNode childSchema = path.getLast();
+ validateNode(childElement, childSchema, subtree);
+ } catch (URISyntaxException | MissingNameSpaceException e) {
+ throw new RuntimeException("Wrong namespace in element + " + childElement.toString());
+ }
+ }
+ return tree;
+ }
+
+ /**
+ * Searches for YangInstanceIdentifier of node, which can be used as root for data selection.
+ * It goes as deep in tree as possible. Method stops traversing, when there are multiple child elements
+ * or when it encounters list node.
+ * @param tree QName tree
+ * @param builder builder
+ * @return YangInstanceIdentifier
+ */
+ private YangInstanceIdentifier getFilterDataRoot(FilterTree tree, YangInstanceIdentifier.InstanceIdentifierBuilder builder) {
+ builder.node(tree.getName());
+ while (tree.getChildren().size() == 1) {
+ final FilterTree child = tree.getChildren().iterator().next();
+ if (child.getType() == Type.CHOICE_CASE) {
+ tree = child;
+ continue;
+ }
+ builder.node(child.getName());
+ if (child.getType() == Type.LIST) {
+ return builder.build();
+ }
+ tree = child;
+ }
+ return builder.build();
+ }
+
+ //FIXME this method will also be in yangtools ParserUtils, use that when https://git.opendaylight.org/gerrit/#/c/37031/ will be merged
+ /**
+ * Returns stack of schema nodes via which it was necessary to pass to get schema node with specified
+ * {@code childName} and {@code namespace}
+ *
+ * @param dataSchemaNode
+ * @param childName
+ * @param namespace
+ * @return stack of schema nodes via which it was passed through. If found schema node is direct child then stack
+ * contains only one node. If it is found under choice and case then stack should contains 2*n+1 element
+ * (where n is number of choices through it was passed)
+ */
+ private Deque<DataSchemaNode> findSchemaNodeByNameAndNamespace(final DataSchemaNode dataSchemaNode,
+ final String childName, final URI namespace) {
+ final Deque<DataSchemaNode> result = new ArrayDeque<>();
+ final List<ChoiceSchemaNode> childChoices = new ArrayList<>();
+ DataSchemaNode potentialChildNode = null;
+ if (dataSchemaNode instanceof DataNodeContainer) {
+ for (final DataSchemaNode childNode : ((DataNodeContainer) dataSchemaNode).getChildNodes()) {
+ if (childNode instanceof ChoiceSchemaNode) {
+ childChoices.add((ChoiceSchemaNode) childNode);
+ } else {
+ final QName childQName = childNode.getQName();
+
+ if (childQName.getLocalName().equals(childName) && childQName.getNamespace().equals(namespace)) {
+ if (potentialChildNode == null ||
+ childQName.getRevision().after(potentialChildNode.getQName().getRevision())) {
+ potentialChildNode = childNode;
+ }
+ }
+ }
+ }
+ }
+ if (potentialChildNode != null) {
+ result.push(potentialChildNode);
+ return result;
+ }
+
+ // try to find data schema node in choice (looking for first match)
+ for (final ChoiceSchemaNode choiceNode : childChoices) {
+ for (final ChoiceCaseNode concreteCase : choiceNode.getCases()) {
+ final Deque<DataSchemaNode> resultFromRecursion = findSchemaNodeByNameAndNamespace(concreteCase, childName,
+ namespace);
+ if (!resultFromRecursion.isEmpty()) {
+ resultFromRecursion.push(concreteCase);
+ resultFromRecursion.push(choiceNode);
+ return resultFromRecursion;
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Class represents tree of QNames as they are present in the filter.
+ */
+ private static class FilterTree {
+
+ private final QName name;
+ private final Type type;
+ private final Map<QName, FilterTree> children;
+
+ FilterTree(QName name, Type type) {
+ this.name = name;
+ this.type = type;
+ this.children = new HashMap<>();
+ }
+
+ FilterTree addChild(DataSchemaNode data) {
+ Type type;
+ if (data instanceof ChoiceCaseNode) {
+ type = Type.CHOICE_CASE;
+ } else if (data instanceof ListSchemaNode) {
+ type = Type.LIST;
+ } else {
+ type = Type.OTHER;
+ }
+ final QName name = data.getQName();
+ FilterTree childTree = children.get(name);
+ if (childTree == null) {
+ childTree = new FilterTree(name, type);
+ }
+ children.put(name, childTree);
+ return childTree;
+ }
+
+ Collection<FilterTree> getChildren() {
+ return children.values();
+ }
+
+ QName getName() {
+ return name;
+ }
+
+ Type getType() {
+ return type;
+ }
+ }
+
+ private enum Type {
+ LIST, CHOICE_CASE, OTHER
+ }
+
+ private static class ValidationException extends Exception {
+ public ValidationException(XmlElement parent, XmlElement child) {
+ super("Element " + child + " can't be child of " + parent);
+ }
+ }
+
+}
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.doAnswer;
+import com.google.common.io.ByteSource;
import com.google.common.util.concurrent.Futures;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
-import java.util.Collections;
import java.util.EnumMap;
import java.util.List;
import java.util.concurrent.ExecutorService;
-
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
-
import org.custommonkey.xmlunit.DetailedDiff;
import org.custommonkey.xmlunit.Diff;
import org.custommonkey.xmlunit.XMLUnit;
import org.opendaylight.yangtools.util.concurrent.SpecialExecutors;
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier;
-import org.opendaylight.yangtools.yang.data.api.YangInstanceIdentifier.AugmentationIdentifier;
import org.opendaylight.yangtools.yang.model.api.Module;
import org.opendaylight.yangtools.yang.model.api.SchemaContext;
import org.opendaylight.yangtools.yang.model.api.SchemaContextListener;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
-import com.google.common.io.ByteSource;
-
public class NetconfMDSalMappingTest {
private static final Logger LOG = LoggerFactory.getLogger(NetconfMDSalMappingTest.class);
private static final QName INNER_CHOICE_TEXT = QName.create("urn:opendaylight:mdsal:mapping:test", "2015-02-26", "text");
private static final YangInstanceIdentifier AUGMENTED_CONTAINER_IN_MODULES =
- YangInstanceIdentifier.builder().node(TOP).node(MODULES).build().node(new AugmentationIdentifier(Collections.singleton(AUGMENTED_CONTAINER)));
+ YangInstanceIdentifier.builder().node(TOP).node(MODULES).build();
private static Document RPC_REPLY_OK = null;
verifyResponse(edit("messages/mapping/editConfigs/editConfig-filtering-setup.xml"), RPC_REPLY_OK);
verifyResponse(commit(), RPC_REPLY_OK);
- //TODO uncomment these tests once we can parse KeyedListNode as a selection node, currently you cannot use a KeyedList as a selection node in filter
-// verifyFilterIdentifier("messages/mapping/filters/get-filter-alluser.xml",
-// YangInstanceIdentifier.builder().node(TOP).node(USERS).node(USER).build());
+ verifyFilterIdentifier("messages/mapping/filters/get-filter-alluser.xml",
+ YangInstanceIdentifier.builder().node(TOP).node(USERS).node(USER).build());
verifyFilterIdentifier("messages/mapping/filters/get-filter-company-info.xml",
YangInstanceIdentifier.builder().node(TOP).node(USERS).node(USER).build());
verifyFilterIdentifier("messages/mapping/filters/get-filter-modules-and-admin.xml",
YangInstanceIdentifier.builder().node(TOP).build());
verifyFilterIdentifier("messages/mapping/filters/get-filter-only-names-types.xml",
YangInstanceIdentifier.builder().node(TOP).node(USERS).node(USER).build());
-// verifyFilterIdentifier("messages/mapping/filters/get-filter-specific-module-type-and-user.xml",
-// YangInstanceIdentifier.builder().node(TOP).build());
-// verifyFilterIdentifier("messages/mapping/filters/get-filter-superuser.xml",
-// YangInstanceIdentifier.builder().node(TOP).node(USERS).node(USER).build());
+ verifyFilterIdentifier("messages/mapping/filters/get-filter-specific-module-type-and-user.xml",
+ YangInstanceIdentifier.builder().node(TOP).build());
+ verifyFilterIdentifier("messages/mapping/filters/get-filter-superuser.xml",
+ YangInstanceIdentifier.builder().node(TOP).node(USERS).node(USER).build());
verifyFilterIdentifier("messages/mapping/filters/get-filter-users.xml",
YangInstanceIdentifier.builder().node(TOP).node(USERS).build());
//verifyResponse(edit("messages/mapping/editConfigs/editConfig-filtering-setup2.xml"), RPC_REPLY_OK);
//verifyResponse(commit(), RPC_REPLY_OK);
-// verifyFilterIdentifier("messages/mapping/filters/get-filter-augmented-case-inner-choice.xml",
-// YangInstanceIdentifier.builder().node(TOP).node(CHOICE_NODE).node(CHOICE_WRAPPER).build());
-// verifyFilterIdentifier("messages/mapping/filters/get-filter-augmented-case-inner-case.xml",
-// YangInstanceIdentifier.builder().node(TOP).node(CHOICE_NODE).node(CHOICE_WRAPPER).node(INNER_CHOICE).node(INNER_CHOICE_TEXT).build());
+ verifyFilterIdentifier("messages/mapping/filters/get-filter-augmented-case-inner-choice.xml",
+ YangInstanceIdentifier.builder().node(TOP).node(CHOICE_NODE).node(CHOICE_WRAPPER).build());
+ verifyFilterIdentifier("messages/mapping/filters/get-filter-augmented-case-inner-case.xml",
+ YangInstanceIdentifier.builder().node(TOP).node(CHOICE_NODE).node(CHOICE_WRAPPER).node(INNER_CHOICE).node(INNER_CHOICE_TEXT).build());
// verifyResponse(getConfigWithFilter("messages/mapping/filters/get-filter-augmented-string.xml"),
// XmlFileLoader.xmlFileToDocument("messages/mapping/filters/response-augmented-string.xml"));
TestingGetConfig getConfig = new TestingGetConfig(sessionIdForReporting, currentSchemaContext, transactionProvider);
Document request = XmlFileLoader.xmlFileToDocument(resource);
YangInstanceIdentifier iid = getConfig.getInstanceIdentifierFromDocument(request);
- assertTrue(iid.equals(identifier));
+ assertEquals(identifier, iid);
}
private class TestingGetConfig extends GetConfig{
--- /dev/null
+package org.opendaylight.netconf.mdsal.connector.ops.get;
+
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URISyntaxException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+import org.junit.runners.model.InitializationError;
+import org.opendaylight.controller.config.util.xml.XmlElement;
+import org.opendaylight.controller.config.util.xml.XmlUtil;
+import org.opendaylight.netconf.mdsal.connector.CurrentSchemaContext;
+import org.opendaylight.yangtools.yang.model.api.SchemaContext;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+import org.opendaylight.yangtools.yang.parser.stmt.reactor.CrossSourceStatementReactor;
+import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangInferencePipeline;
+import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.YangStatementSourceImpl;
+import org.w3c.dom.Document;
+import org.xml.sax.SAXException;
+
+@RunWith(value = Parameterized.class)
+public class FilterContentValidatorTest {
+
+ private static final int TEST_CASE_COUNT = 8;
+ private final XmlElement filterContent;
+ private final String expected;
+ private FilterContentValidator validator;
+
+ @Parameterized.Parameters
+ public static Collection<Object[]> data() throws IOException, SAXException, URISyntaxException, InitializationError {
+ List<Object[]> result = new ArrayList<>();
+ final Path path = Paths.get(FilterContentValidatorTest.class.getResource("/filter/expected.txt").toURI());
+ final List<String> expected = Files.readAllLines(path);
+ if (expected.size() != TEST_CASE_COUNT) {
+ throw new InitializationError("Number of lines in results file must be same as test case count");
+ }
+ for (int i = 1; i <= TEST_CASE_COUNT; i++) {
+ final Document document = XmlUtil.readXmlToDocument(FilterContentValidatorTest.class.getResourceAsStream("/filter/f" + i + ".xml"));
+ result.add(new Object[]{document, expected.get(i-1)});
+ }
+ return result;
+ }
+
+ public FilterContentValidatorTest(Document filterContent, String expected) {
+ this.filterContent = XmlElement.fromDomDocument(filterContent);
+ this.expected = expected;
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ List<InputStream> sources = new ArrayList<>();
+ sources.add(getClass().getResourceAsStream("/yang/filter-validator-test-mod-0.yang"));
+ sources.add(getClass().getResourceAsStream("/yang/filter-validator-test-augment.yang"));
+ SchemaContext context = parseYangSources(sources);
+ CurrentSchemaContext currentContext = mock(CurrentSchemaContext.class);
+ doReturn(context).when(currentContext).getCurrentContext();
+ validator = new FilterContentValidator(currentContext);
+ }
+
+ @Test
+ public void testValidate() throws Exception {
+ if (expected.startsWith("success")) {
+ final String expId = expected.replace("success=", "");
+ Assert.assertEquals(expId, validator.validate(filterContent).toString());
+ } else if (expected.startsWith("error")) {
+ try {
+ validator.validate(filterContent);
+ Assert.fail(XmlUtil.toString(filterContent) + " is not valid and should throw exception.");
+ } catch (Exception e) {
+ final String expectedExceptionClass = expected.replace("error=", "");
+ Assert.assertEquals(expectedExceptionClass, e.getClass().getName());
+ }
+ }
+
+ }
+
+ public static SchemaContext parseYangSources(Collection<InputStream> testFiles)
+ throws SourceException, ReactorException, FileNotFoundException {
+ CrossSourceStatementReactor.BuildAction reactor = YangInferencePipeline.RFC6020_REACTOR
+ .newBuild();
+ for (InputStream testFile : testFiles) {
+ reactor.addSource(new YangStatementSourceImpl(testFile));
+ }
+ return reactor.buildEffective();
+ }
+}
\ No newline at end of file
--- /dev/null
+error=org.opendaylight.controller.config.util.xml.DocumentedException
+success=/(urn:dummy:mod-0?revision=2016-03-01)mainroot
+success=/(urn:dummy:mod-0?revision=2016-03-01)mainroot/choiceList
+success=/(urn:dummy:mod-0?revision=2016-03-01)mainroot/maincontent
+success=/(urn:dummy:mod-0?revision=2016-03-01)mainroot/choiceList
+success=/(urn:dummy:mod-0?revision=2016-03-01)mainroot
+success=/(urn:dummy:mod-0?revision=2016-03-01)mainroot/(urn:dummy:aug?revision=1999-08-17)augmented-leaf
+error=org.opendaylight.controller.config.util.xml.DocumentedException
\ No newline at end of file
--- /dev/null
+<mainroot xmlns="urn:dummy:mod-0">
+ <maincontent></maincontent>
+ <not-in-schema/>
+</mainroot>
\ No newline at end of file
--- /dev/null
+<mainroot xmlns="urn:dummy:mod-0">
+ <maincontent/>
+ <choiceList>
+ <choice-leaf>
+
+ </choice-leaf>
+ </choiceList>
+</mainroot>
\ No newline at end of file
--- /dev/null
+<mainroot xmlns="urn:dummy:mod-0">
+ <choiceList>
+ <choice-leaf>
+ aaa
+ </choice-leaf>
+ </choiceList>
+ <choiceList>
+ <choice-leaf>
+ bbb
+ </choice-leaf>
+ </choiceList>
+</mainroot>
\ No newline at end of file
--- /dev/null
+<mainroot xmlns="urn:dummy:mod-0">
+ <maincontent/>
+</mainroot>
\ No newline at end of file
--- /dev/null
+<mainroot xmlns="urn:dummy:mod-0">
+ <choiceList></choiceList>
+</mainroot>
\ No newline at end of file
--- /dev/null
+<mainroot xmlns="urn:dummy:mod-0">
+ <maincontent/>
+ <choiceList></choiceList>
+</mainroot>
\ No newline at end of file
--- /dev/null
+<mainroot xmlns="urn:dummy:mod-0">
+ <augmented-leaf xmlns="urn:dummy:aug"/>
+</mainroot>
\ No newline at end of file
--- /dev/null
+<mainroot xmlns="urn:dummy:mod-0">
+ <maincontent>
+ <choiceList>
+ </choiceList>
+ </maincontent>
+</mainroot>
\ No newline at end of file
--- /dev/null
+module filter-validator-test-augment {
+ namespace "urn:dummy:aug";
+ prefix "aug-0";
+ revision "1999-08-17";
+ import filter-validator-test-mod-0 {
+ prefix mod-0;
+ revision-date "2016-03-01";
+ }
+ augment "/mod-0:mainroot" {
+ leaf augmented-leaf {
+ type string;
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+module filter-validator-test-mod-0 {
+ namespace "urn:dummy:mod-0";
+ prefix "mod-0";
+ revision "2016-03-01";
+ container mainroot {
+ leaf maincontent {
+ mandatory true;
+ type string;
+ }
+ list choiceList {
+ key name;
+ leaf name {
+ type string;
+ }
+ choice v {
+ case a {
+ leaf choice-leaf {
+ type string;
+ }
+ }
+ case b {
+ container choice-cont {
+ leaf content {
+ type string;
+ }
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file