From: Peter Kajsa Date: Thu, 29 Jun 2017 08:34:12 +0000 (+0200) Subject: Bug 2444 - Add missing API to Action and Notification definition X-Git-Tag: v2.0.0~205 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=commitdiff_plain;h=b392ed1eb50e13d298496fed050cd617c04fa14a;p=yangtools.git Bug 2444 - Add missing API to Action and Notification definition Since Yang 1.1, it is allowed to use notification and action statements also in grouping and augment statements. However, yang-model-api does not reflect this changes. This patch adds missing methods to check the origin of notifications and actions. Required fix of yang-model-export is included as well. Change-Id: Iba7282e3be4361bba8e6328a57fbbe0cb115a1f5 Signed-off-by: Peter Kajsa --- diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/ActionDefinition.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/ActionDefinition.java index 3940ee0db6..f304b2d2d8 100644 --- a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/ActionDefinition.java +++ b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/ActionDefinition.java @@ -20,6 +20,5 @@ import com.google.common.annotations.Beta; * detailed action information. The argument is the name of the action. */ @Beta -public interface ActionDefinition extends OperationDefinition { - +public interface ActionDefinition extends OperationDefinition, CopyableNode { } diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/CopyableNode.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/CopyableNode.java new file mode 100644 index 0000000000..4dba5a251d --- /dev/null +++ b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/CopyableNode.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2017 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.model.api; + +import com.google.common.annotations.Beta; + +/** + * Represents a node that can be added by uses or by augmentation. + */ +@Beta +public interface CopyableNode { + /** + * Returns true if this node was added by augmentation, + * otherwise returns false. + * + * @return true if this node was added by augmentation, + * otherwise returns false + */ + boolean isAugmenting(); + + /** + * Returns true if this node was added by uses statement, + * otherwise returns false. + * + * @return true if this node was added by uses statement, + * otherwise returns false + */ + boolean isAddedByUses(); +} diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/DataSchemaNode.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/DataSchemaNode.java index 4c27eacd47..3bf46fa617 100644 --- a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/DataSchemaNode.java +++ b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/DataSchemaNode.java @@ -24,25 +24,7 @@ package org.opendaylight.yangtools.yang.model.api; * @see AnyXmlSchemaNode * @see AnyDataSchemaNode */ -public interface DataSchemaNode extends SchemaNode { - /** - * Returns true if the data node was added by augmentation, - * otherwise returns false. - * - * @return true if the data node was added by augmentation, - * otherwise returns false - */ - boolean isAugmenting(); - - /** - * Returns true if the data node was added by uses statement, - * otherwise returns false. - * - * @return true if the data node was added by uses statement, - * otherwise returns false - */ - boolean isAddedByUses(); - +public interface DataSchemaNode extends SchemaNode, CopyableNode { /** * Returns true if the data represents configuration data, * otherwise returns false. diff --git a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/NotificationDefinition.java b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/NotificationDefinition.java index 06683d9612..95d1dd3cb8 100644 --- a/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/NotificationDefinition.java +++ b/yang/yang-model-api/src/main/java/org/opendaylight/yangtools/yang/model/api/NotificationDefinition.java @@ -13,7 +13,7 @@ import javax.annotation.Nullable; * Interface describing YANG 'notification' statement. The notification * statement is used to define a NETCONF notification. */ -public interface NotificationDefinition extends SchemaNode, DataNodeContainer, AugmentationTarget { +public interface NotificationDefinition extends SchemaNode, DataNodeContainer, AugmentationTarget, CopyableNode { /** * All implementations should override this method. diff --git a/yang/yang-model-export/src/main/java/org/opendaylight/yangtools/yang/model/export/SchemaContextEmitter.java b/yang/yang-model-export/src/main/java/org/opendaylight/yangtools/yang/model/export/SchemaContextEmitter.java index 9a37e3ecfd..ebfb5acfaf 100644 --- a/yang/yang-model-export/src/main/java/org/opendaylight/yangtools/yang/model/export/SchemaContextEmitter.java +++ b/yang/yang-model-export/src/main/java/org/opendaylight/yangtools/yang/model/export/SchemaContextEmitter.java @@ -1810,9 +1810,6 @@ abstract class SchemaContextEmitter { private void emitContainer(final ContainerSchemaNode child) { super.writer.startContainerNode(child.getQName()); - - // - emitConstraints(child.getConstraints()); // FIXME: BUG-2444: whenNode //:Optional // FIXME: BUG-2444: *(ifFeatureNode ) @@ -2229,8 +2226,10 @@ abstract class SchemaContextEmitter { } private void emitAction(final ActionDefinition action) { - // :FIXME add addedByUses & addedByAugmentation in API and perform - // check here.. + if (!super.emitInstantiated && (action.isAddedByUses() || action.isAugmenting())) { + // We skip instantiated nodes. + return; + } super.writer.startActionNode(action.getQName()); emitOperationBody(action); super.writer.endNode(); @@ -2269,8 +2268,10 @@ abstract class SchemaContextEmitter { } private void emitNotificationNode(final NotificationDefinition notification) { - // :FIXME add addedByUses & addedByAugmentation in API and perform - // check here.. + if (!super.emitInstantiated && (notification.isAddedByUses() || notification.isAugmenting())) { + // We skip instantiated nodes. + return; + } super.writer.startNotificationNode(notification.getQName()); // FIXME: BUG-2444: *(ifFeatureNode ) diff --git a/yang/yang-model-export/src/test/java/org/opendaylight/yangtools/yang/model/export/EffectiveSchemaContextEmitterTest.java b/yang/yang-model-export/src/test/java/org/opendaylight/yangtools/yang/model/export/EffectiveSchemaContextEmitterTest.java new file mode 100644 index 0000000000..00b7403155 --- /dev/null +++ b/yang/yang-model-export/src/test/java/org/opendaylight/yangtools/yang/model/export/EffectiveSchemaContextEmitterTest.java @@ -0,0 +1,135 @@ +/* + * Copyright (c) 2017 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.model.export; + +import static org.junit.Assert.assertNotEquals; +import static org.junit.Assert.assertNotNull; + +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; +import java.io.BufferedOutputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.net.URI; +import java.util.Map; +import javax.xml.stream.XMLOutputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; +import org.custommonkey.xmlunit.Diff; +import org.custommonkey.xmlunit.ElementNameAndAttributeQualifier; +import org.custommonkey.xmlunit.XMLAssert; +import org.custommonkey.xmlunit.XMLUnit; +import org.junit.Test; +import org.opendaylight.yangtools.yang.common.QName; +import org.opendaylight.yangtools.yang.common.SimpleDateFormatUtil; +import org.opendaylight.yangtools.yang.common.YangVersion; +import org.opendaylight.yangtools.yang.model.api.Module; +import org.opendaylight.yangtools.yang.model.api.ModuleImport; +import org.opendaylight.yangtools.yang.model.api.SchemaContext; +import org.opendaylight.yangtools.yang.model.api.meta.StatementDefinition; +import org.opendaylight.yangtools.yang.model.export.SchemaContextEmitter.EffectiveSchemaContextEmitter; +import org.opendaylight.yangtools.yang.model.export.test.YinExportTestUtils; +import org.opendaylight.yangtools.yang.test.util.YangParserTestUtils; +import org.w3c.dom.Document; +import org.xml.sax.SAXException; + +public class EffectiveSchemaContextEmitterTest { + @Test + public void test() throws Exception { + final SchemaContext schema = YangParserTestUtils.parseYangResource("/bugs/bug2444/yang/notification.yang"); + assertNotNull(schema); + + final File outDir = new File("target/bug2444-export"); + outDir.mkdirs(); + + for (final Module module : schema.getModules()) { + exportModule(schema, module, outDir); + final OutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + final BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(byteArrayOutputStream); + try { + writeModuleToOutputStream(schema, module, bufferedOutputStream, false); + final String output = byteArrayOutputStream.toString(); + assertNotNull(output); + assertNotEquals(0, output.length()); + + final Document doc = YinExportTestUtils + .loadDocument(String.format("/bugs/bug2444/yin-effective-emitter/%s@%s.yin", module.getName(), + SimpleDateFormatUtil.getRevisionFormat().format(module.getRevision()))); + assertXMLEquals(doc, output); + } finally { + byteArrayOutputStream.close(); + bufferedOutputStream.close(); + } + } + } + + private static void writeModuleToOutputStream(final SchemaContext ctx, final Module module, final OutputStream str, + final boolean emitInstantiated) throws XMLStreamException { + final XMLOutputFactory factory = XMLOutputFactory.newFactory(); + final XMLStreamWriter xmlStreamWriter = factory.createXMLStreamWriter(str); + writeModuleToOutputStream(ctx, module, xmlStreamWriter, emitInstantiated); + xmlStreamWriter.flush(); + } + + private static void writeModuleToOutputStream(final SchemaContext ctx, final Module module, + final XMLStreamWriter xmlStreamWriter, final boolean emitInstantiated) { + final URI moduleNs = module.getNamespace(); + final Map prefixToNs = prefixToNamespace(ctx, module); + final StatementTextWriter statementWriter = SingleModuleYinStatementWriter.create(xmlStreamWriter, moduleNs, + prefixToNs); + final YangModuleWriter yangSchemaWriter = SchemaToStatementWriterAdaptor.from(statementWriter); + final Map extensions = ExtensionStatement.mapFrom(ctx.getExtensions()); + new EffectiveSchemaContextEmitter(yangSchemaWriter, extensions, + YangVersion.parse(module.getYangVersion()).orElse(null), emitInstantiated).emitModule(module); + } + + private static Map prefixToNamespace(final SchemaContext ctx, final Module module) { + final BiMap prefixMap = HashBiMap.create(module.getImports().size() + 1); + prefixMap.put(module.getPrefix(), module.getNamespace()); + for (final ModuleImport imp : module.getImports()) { + final String prefix = imp.getPrefix(); + final URI namespace = getModuleNamespace(ctx, imp.getModuleName()); + prefixMap.put(prefix, namespace); + } + return prefixMap; + } + + private static URI getModuleNamespace(final SchemaContext ctx, final String moduleName) { + for (final Module module : ctx.getModules()) { + if (moduleName.equals(module.getName())) { + return module.getNamespace(); + } + } + throw new IllegalArgumentException("Module " + moduleName + "does not exists in provided schema context"); + } + + private static File exportModule(final SchemaContext schemaContext, final Module module, final File outDir) + throws Exception { + final File outFile = new File(outDir, YinExportUtils.wellFormedYinName(module.getName(), module.getRevision())); + try (OutputStream output = new FileOutputStream(outFile)) { + writeModuleToOutputStream(schemaContext, module, output, false); + } + return outFile; + } + + private static void assertXMLEquals(final Document expectedXMLDoc, final String output) + throws SAXException, IOException { + final String expected = YinExportTestUtils.toString(expectedXMLDoc.getDocumentElement()); + + XMLUnit.setIgnoreWhitespace(true); + XMLUnit.setNormalize(true); + XMLUnit.setNormalizeWhitespace(true); + + final Diff diff = new Diff(expected, output); + diff.overrideElementQualifier(new ElementNameAndAttributeQualifier()); + XMLAssert.assertXMLEqual(diff, true); + } +} diff --git a/yang/yang-model-export/src/test/java/org/opendaylight/yangtools/yang/model/export/test/Bug2444Test.java b/yang/yang-model-export/src/test/java/org/opendaylight/yangtools/yang/model/export/test/Bug2444Test.java index 34ceedb09f..d96f600146 100644 --- a/yang/yang-model-export/src/test/java/org/opendaylight/yangtools/yang/model/export/test/Bug2444Test.java +++ b/yang/yang-model-export/src/test/java/org/opendaylight/yangtools/yang/model/export/test/Bug2444Test.java @@ -55,7 +55,7 @@ public class Bug2444Test { } } - private ImmutableSet getAllModulesAndSubmodules(final SchemaContext schema) { + private static ImmutableSet getAllModulesAndSubmodules(final SchemaContext schema) { final Builder builder = ImmutableSet.builder(); builder.addAll(schema.getModules()); for (final Module module : schema.getModules()) { diff --git a/yang/yang-model-export/src/test/java/org/opendaylight/yangtools/yang/model/export/test/YinExportTestUtils.java b/yang/yang-model-export/src/test/java/org/opendaylight/yangtools/yang/model/export/test/YinExportTestUtils.java index 6141bcfb32..7cf35e8d79 100644 --- a/yang/yang-model-export/src/test/java/org/opendaylight/yangtools/yang/model/export/test/YinExportTestUtils.java +++ b/yang/yang-model-export/src/test/java/org/opendaylight/yangtools/yang/model/export/test/YinExportTestUtils.java @@ -29,7 +29,7 @@ public class YinExportTestUtils { throw new UnsupportedOperationException("Utility class"); } - static Document loadDocument(final String xmlPath) throws IOException, SAXException { + public static Document loadDocument(final String xmlPath) throws IOException, SAXException { final InputStream resourceAsStream = SchemaContextEmitterTest.class.getResourceAsStream(xmlPath); final Document currentConfigElement = readXmlToDocument(resourceAsStream); Preconditions.checkNotNull(currentConfigElement); @@ -42,7 +42,7 @@ public class YinExportTestUtils { return doc; } - static String toString(final Node xml) { + public static String toString(final Node xml) { try { final Transformer transformer = TransformerFactory.newInstance().newTransformer(); transformer.setOutputProperty(OutputKeys.INDENT, "yes"); diff --git a/yang/yang-model-export/src/test/resources/bugs/bug2444/yin-effective-emitter/notification@1970-01-01.yin b/yang/yang-model-export/src/test/resources/bugs/bug2444/yin-effective-emitter/notification@1970-01-01.yin new file mode 100644 index 0000000000..8b996e194f --- /dev/null +++ b/yang/yang-model-export/src/test/resources/bugs/bug2444/yin-effective-emitter/notification@1970-01-01.yin @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/rfc6020/effective/NotificationEffectiveStatementImpl.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/rfc6020/effective/NotificationEffectiveStatementImpl.java index 3eae1c0bef..32dedfe635 100644 --- a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/rfc6020/effective/NotificationEffectiveStatementImpl.java +++ b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/rfc6020/effective/NotificationEffectiveStatementImpl.java @@ -23,6 +23,8 @@ import org.opendaylight.yangtools.yang.model.api.SchemaPath; import org.opendaylight.yangtools.yang.model.api.UnknownSchemaNode; import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement; import org.opendaylight.yangtools.yang.model.api.stmt.NotificationStatement; +import org.opendaylight.yangtools.yang.parser.spi.meta.CopyHistory; +import org.opendaylight.yangtools.yang.parser.spi.meta.CopyType; import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext; public class NotificationEffectiveStatementImpl extends @@ -32,6 +34,8 @@ public class NotificationEffectiveStatementImpl extends private final ConstraintDefinition constraints; private final Set augmentations; private final List unknownNodes; + private final boolean augmenting; + private final boolean addedByUses; public NotificationEffectiveStatementImpl( final StmtContext> ctx) { @@ -42,20 +46,29 @@ public class NotificationEffectiveStatementImpl extends this.constraints = EffectiveConstraintDefinitionImpl.forParent(this); // initSubstatementCollections - List unknownNodesInit = new ArrayList<>(); - Set augmentationsInit = new LinkedHashSet<>(); - for (EffectiveStatement effectiveStatement : effectiveSubstatements()) { + final List unknownNodesInit = new ArrayList<>(); + final Set augmentationsInit = new LinkedHashSet<>(); + for (final EffectiveStatement effectiveStatement : effectiveSubstatements()) { if (effectiveStatement instanceof UnknownSchemaNode) { - UnknownSchemaNode unknownNode = (UnknownSchemaNode) effectiveStatement; + final UnknownSchemaNode unknownNode = (UnknownSchemaNode) effectiveStatement; unknownNodesInit.add(unknownNode); } if (effectiveStatement instanceof AugmentationSchema) { - AugmentationSchema augmentationSchema = (AugmentationSchema) effectiveStatement; + final AugmentationSchema augmentationSchema = (AugmentationSchema) effectiveStatement; augmentationsInit.add(augmentationSchema); } } this.unknownNodes = ImmutableList.copyOf(unknownNodesInit); this.augmentations = ImmutableSet.copyOf(augmentationsInit); + + // initCopyType + final CopyHistory copyTypesFromOriginal = ctx.getCopyHistory(); + if (copyTypesFromOriginal.contains(CopyType.ADDED_BY_USES_AUGMENTATION)) { + this.addedByUses = this.augmenting = true; + } else { + this.augmenting = copyTypesFromOriginal.contains(CopyType.ADDED_BY_AUGMENTATION); + this.addedByUses = copyTypesFromOriginal.contains(CopyType.ADDED_BY_USES); + } } @Nonnull @@ -86,6 +99,16 @@ public class NotificationEffectiveStatementImpl extends return unknownNodes; } + @Override + public boolean isAugmenting() { + return augmenting; + } + + @Override + public boolean isAddedByUses() { + return addedByUses; + } + @Override public int hashCode() { final int prime = 31; diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/effective/ActionEffectiveStatementImpl.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/effective/ActionEffectiveStatementImpl.java index 1a4ee61bed..18d5a02f64 100644 --- a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/effective/ActionEffectiveStatementImpl.java +++ b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/rfc7950/effective/ActionEffectiveStatementImpl.java @@ -21,6 +21,8 @@ import org.opendaylight.yangtools.yang.model.api.GroupingDefinition; import org.opendaylight.yangtools.yang.model.api.TypeDefinition; import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement; import org.opendaylight.yangtools.yang.model.api.stmt.ActionStatement; +import org.opendaylight.yangtools.yang.parser.spi.meta.CopyHistory; +import org.opendaylight.yangtools.yang.parser.spi.meta.CopyType; import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext; import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.effective.AbstractEffectiveSchemaNode; import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.effective.EffectiveStmtUtils; @@ -34,6 +36,8 @@ public class ActionEffectiveStatementImpl extends AbstractEffectiveSchemaNode> typeDefinitions; private final Set groupings; + private final boolean augmenting; + private final boolean addedByUses; public ActionEffectiveStatementImpl( final StmtContext> ctx) { @@ -42,16 +46,16 @@ public class ActionEffectiveStatementImpl extends AbstractEffectiveSchemaNode groupingsInit = new HashSet<>(); - Set> mutableTypeDefinitions = new LinkedHashSet<>(); - for (EffectiveStatement effectiveStatement : effectiveSubstatements()) { + final Set groupingsInit = new HashSet<>(); + final Set> mutableTypeDefinitions = new LinkedHashSet<>(); + for (final EffectiveStatement effectiveStatement : effectiveSubstatements()) { if (effectiveStatement instanceof GroupingDefinition) { - GroupingDefinition groupingDefinition = (GroupingDefinition) effectiveStatement; + final GroupingDefinition groupingDefinition = (GroupingDefinition) effectiveStatement; groupingsInit.add(groupingDefinition); } if (effectiveStatement instanceof TypeDefEffectiveStatementImpl) { - TypeDefEffectiveStatementImpl typeDef = (TypeDefEffectiveStatementImpl) effectiveStatement; - TypeDefinition type = typeDef.getTypeDefinition(); + final TypeDefEffectiveStatementImpl typeDef = (TypeDefEffectiveStatementImpl) effectiveStatement; + final TypeDefinition type = typeDef.getTypeDefinition(); if (!mutableTypeDefinitions.contains(type)) { mutableTypeDefinitions.add(type); } else { @@ -61,6 +65,15 @@ public class ActionEffectiveStatementImpl extends AbstractEffectiveSchemaNode