@Beta
public interface ActionEffectiveStatement extends SchemaTreeEffectiveStatement<ActionStatement>,
- SchemaTreeAwareEffectiveStatement<QName, ActionStatement> {
+ DataTreeAwareEffectiveStatement<QName, ActionStatement> {
}
import com.google.common.annotations.Beta;
@Beta
-public interface AnydataEffectiveStatement extends SchemaTreeEffectiveStatement<AnydataStatement> {
+public interface AnydataEffectiveStatement extends DataTreeEffectiveStatement<AnydataStatement> {
}
import com.google.common.annotations.Beta;
@Beta
-public interface AnyxmlEffectiveStatement extends SchemaTreeEffectiveStatement<AnyxmlStatement> {
+public interface AnyxmlEffectiveStatement extends DataTreeEffectiveStatement<AnyxmlStatement> {
}
import org.opendaylight.yangtools.yang.common.QName;
@Beta
-public interface ContainerEffectiveStatement extends SchemaTreeEffectiveStatement<ContainerStatement>,
- SchemaTreeAwareEffectiveStatement<QName, ContainerStatement> {
+public interface ContainerEffectiveStatement extends DataTreeEffectiveStatement<ContainerStatement>,
+ DataTreeAwareEffectiveStatement<QName, ContainerStatement> {
}
--- /dev/null
+/*
+ * Copyright (c) 2018 Pantheon Technologies, 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.model.api.stmt;
+
+import static java.util.Objects.requireNonNull;
+
+import com.google.common.annotations.Beta;
+import java.util.Optional;
+import org.eclipse.jdt.annotation.NonNull;
+import org.eclipse.jdt.annotation.NonNullByDefault;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+
+/**
+ * Interface implemented by all {@link SchemaTreeAwareEffectiveStatement}s which can contain a {@code data tree} child.
+ *
+ * @param <A> Argument type
+ * @param <D> Class representing declared version of this statement.
+ * @author Robert Varga
+ */
+@Beta
+public interface DataTreeAwareEffectiveStatement<A, D extends DeclaredStatement<A>>
+ extends SchemaTreeAwareEffectiveStatement<A, D> {
+
+ /**
+ * Namespace of {@code data node}s. This is a subtree of {@link SchemaTreeAwareEffectiveStatement.Namespace} in that
+ * all data nodes are also schema nodes. The structure of the tree is different, though, as {@code choice}
+ * and {@code case} statements are glossed over and they do not contribute to the tree hierarchy -- only their
+ * children do.
+ *
+ * <p>
+ * This corresponds to the {@code data tree} view of a YANG-defined data.
+ *
+ * @param <T> Child statement type
+ */
+ @NonNullByDefault
+ abstract class Namespace<T extends DataTreeEffectiveStatement<?>> extends EffectiveStatementNamespace<T> {
+ private Namespace() {
+ // Should never be instantiated
+ }
+ }
+
+ /**
+ * Find a {@code data tree} child {@link DataTreeEffectiveStatement}, as identified by its QName argument.
+ *
+ * @param qname Child identifier
+ * @return Data tree child, or empty
+ * @throws NullPointerException if {@code qname} is null
+ */
+ default <E extends DataTreeEffectiveStatement<?>> @NonNull Optional<E> findDataTreeNode(
+ final @NonNull QName qname) {
+ return Optional.ofNullable(get(Namespace.class, requireNonNull(qname)));
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2018 Pantheon Technologies, 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.model.api.stmt;
+
+import com.google.common.annotations.Beta;
+import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
+import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
+
+/**
+ * Common interface grouping all {@link EffectiveStatement}s which are accessible via
+ * {@link DataTreeAwareEffectiveStatement.Namespace}. This such statement corresponds to a {@code data node}.
+ *
+ * <p>
+ * This interface could be named {@code SchemaNodeEffectiveStatement}, but that could induce a notion that it has
+ * something to do with {@link DataSchemaNode} -- which it has not. DataSchemaNode semantics are wrong in may aspects
+ * and while implementations of this interface may also implement DataSchemaNode, the semantics of this interface should
+ * always be preferred and DataSchemaNode is to be treated as deprecated whenever possible.
+ *
+ * @param <D> Declared statement type
+ * @author Robert Varga
+ */
+@Beta
+public interface DataTreeEffectiveStatement<D extends DeclaredStatement<QName>>
+ extends SchemaTreeEffectiveStatement<D> {
+
+}
@Beta
public interface InputEffectiveStatement extends SchemaTreeEffectiveStatement<InputStatement>,
- SchemaTreeAwareEffectiveStatement<QName, InputStatement> {
+ DataTreeAwareEffectiveStatement<QName, InputStatement> {
}
import com.google.common.annotations.Beta;
@Beta
-public interface LeafListEffectiveStatement extends SchemaTreeEffectiveStatement<LeafListStatement> {
+public interface LeafListEffectiveStatement extends DataTreeEffectiveStatement<LeafListStatement> {
}
import org.opendaylight.yangtools.yang.common.QName;
@Beta
-public interface ListEffectiveStatement extends SchemaTreeEffectiveStatement<ListStatement>,
- SchemaTreeAwareEffectiveStatement<QName, ListStatement> {
+public interface ListEffectiveStatement extends DataTreeEffectiveStatement<ListStatement>,
+ DataTreeAwareEffectiveStatement<QName, ListStatement> {
}
// an ordinary String. We really want this to be a QName, so that we do not need the localQNameModule
// bit, but that may be problematic with ModuleStatement, which is getting created before we even know
// the namespace :( A type capture of the string may just be sufficient.
-public interface ModuleEffectiveStatement extends SchemaTreeAwareEffectiveStatement<String, ModuleStatement> {
+public interface ModuleEffectiveStatement extends DataTreeAwareEffectiveStatement<String, ModuleStatement> {
/**
* Namespace mapping all known prefixes in a module to their modules. Note this namespace includes the module
* in which it is instantiated.
@Beta
public interface NotificationEffectiveStatement extends SchemaTreeEffectiveStatement<NotificationStatement>,
- SchemaTreeAwareEffectiveStatement<QName, NotificationStatement> {
+ DataTreeAwareEffectiveStatement<QName, NotificationStatement> {
}
@Beta
public interface OutputEffectiveStatement extends SchemaTreeEffectiveStatement<OutputStatement>,
- SchemaTreeAwareEffectiveStatement<QName, OutputStatement> {
+ DataTreeAwareEffectiveStatement<QName, OutputStatement> {
}
@Beta
public interface RpcEffectiveStatement extends SchemaTreeEffectiveStatement<RpcStatement>,
- SchemaTreeAwareEffectiveStatement<QName, RpcStatement> {
+ DataTreeAwareEffectiveStatement<QName, RpcStatement> {
}
* @return Schema tree child, or empty
* @throws NullPointerException if {@code qname} is null
*/
- // FIXME: make sure this namespace is populated
default <E extends SchemaTreeEffectiveStatement<?>> @NonNull Optional<E> findSchemaTreeNode(
final @NonNull QName qname) {
return Optional.ofNullable(get(Namespace.class, requireNonNull(qname)));
import org.opendaylight.yangtools.yang.common.QName;
import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
+import org.opendaylight.yangtools.yang.model.api.stmt.CaseEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.ChoiceEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.DataTreeAwareEffectiveStatement;
+import org.opendaylight.yangtools.yang.model.api.stmt.DataTreeEffectiveStatement;
import org.opendaylight.yangtools.yang.model.api.stmt.SchemaTreeAwareEffectiveStatement;
import org.opendaylight.yangtools.yang.model.api.stmt.SchemaTreeEffectiveStatement;
import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
@Beta
public abstract class AbstractSchemaEffectiveDocumentedNode<A, D extends DeclaredStatement<A>>
extends AbstractEffectiveDocumentedNode<A, D> {
-
+ private final Map<QName, DataTreeEffectiveStatement<?>> dataTreeNamespace;
private final Map<QName, SchemaTreeEffectiveStatement<?>> schemaTreeNamespace;
protected AbstractSchemaEffectiveDocumentedNode(final StmtContext<A, D, ?> ctx) {
putChild(schemaChildren, child, ref, "schema");
});
schemaTreeNamespace = ImmutableMap.copyOf(schemaChildren);
+
+ if (this instanceof DataTreeAwareEffectiveStatement && !schemaTreeNamespace.isEmpty()) {
+ final Map<QName, DataTreeEffectiveStatement<?>> dataChildren = new LinkedHashMap<>();
+ boolean sameAsSchema = true;
+
+ for (SchemaTreeEffectiveStatement<?> child : schemaTreeNamespace.values()) {
+ if (child instanceof DataTreeEffectiveStatement) {
+ putChild(dataChildren, (DataTreeEffectiveStatement<?>) child, ref, "data");
+ } else {
+ sameAsSchema = false;
+ putChoiceDataChildren(dataChildren, ref, child);
+ }
+ }
+
+ // This is a mighty hack to lower memory usage: if we consumed all schema tree children as data nodes,
+ // the two maps are equal and hence we can share the instance.
+ dataTreeNamespace = sameAsSchema ? (Map) schemaTreeNamespace : ImmutableMap.copyOf(dataChildren);
+ } else {
+ dataTreeNamespace = ImmutableMap.of();
+ }
} else {
+ dataTreeNamespace = ImmutableMap.of();
schemaTreeNamespace = ImmutableMap.of();
}
}
&& SchemaTreeAwareEffectiveStatement.Namespace.class.equals(namespace)) {
return Optional.of((Map<K, V>) schemaTreeNamespace);
}
+ if (this instanceof DataTreeAwareEffectiveStatement
+ && DataTreeAwareEffectiveStatement.Namespace.class.equals(namespace)) {
+ return Optional.of((Map<K, V>) dataTreeNamespace);
+ }
return super.getNamespaceContents(namespace);
}
SourceException.throwIf(prev != null, ref,
"Cannot add %s tree child with name %s, a conflicting child already exists", tree, id);
}
+
+ private static void putChoiceDataChildren(final Map<QName, DataTreeEffectiveStatement<?>> map,
+ final StatementSourceReference ref, final SchemaTreeEffectiveStatement<?> child) {
+ // For choice statements go through all their cases and fetch their data children
+ if (child instanceof ChoiceEffectiveStatement) {
+ child.streamEffectiveSubstatements(CaseEffectiveStatement.class).forEach(
+ caseStmt -> caseStmt.streamEffectiveSubstatements(SchemaTreeEffectiveStatement.class).forEach(stmt -> {
+ if (stmt instanceof DataTreeEffectiveStatement) {
+ putChild(map, (DataTreeEffectiveStatement<?>) stmt, ref, "data");
+ } else {
+ putChoiceDataChildren(map, ref, stmt);
+ }
+ }));
+ }
+ }
}
--- /dev/null
+/*
+ * Copyright (c) 2018 Pantheon Technologies, 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.stmt;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import org.junit.Test;
+import org.opendaylight.yangtools.yang.parser.spi.meta.ReactorException;
+import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
+
+public class NameCollisionWithinCaseTest {
+ @Test
+ public void testChildNameCollisionOfAugmentCase() throws Exception {
+ try {
+ StmtTestUtils.parseYangSource("/bugs/name-collision-within-case/foo.yang");
+ fail("Expected failure due to node name collision");
+ } catch (ReactorException e) {
+ final Throwable cause = e.getCause();
+ assertTrue(cause instanceof SourceException);
+ assertTrue(cause.getMessage().startsWith(
+ "Cannot add data tree child with name (foo?revision=2018-02-11)bar, a conflicting child already exists "
+ + "[at "));
+ }
+ }
+
+ @Test
+ public void testChildNameCollisionOfAugmentChoice() throws Exception {
+ try {
+ StmtTestUtils.parseYangSource("/bugs/name-collision-within-case/bar.yang");
+ fail("Expected failure due to node name collision");
+ } catch (ReactorException e) {
+ final Throwable cause = e.getCause();
+ assertTrue(cause instanceof SourceException);
+ assertTrue(cause.getMessage().startsWith(
+ "Cannot add data tree child with name (bar?revision=2018-02-11)bar, a conflicting child already exists "
+ + "[at "));
+ }
+ }
+
+ @Test
+ public void testChildNameCollisionNormal() throws Exception {
+ try {
+ StmtTestUtils.parseYangSource("/bugs/name-collision-within-case/baz.yang");
+ fail("Expected failure due to node name collision");
+ } catch (ReactorException e) {
+ final Throwable cause = e.getCause();
+ assertTrue(cause instanceof SourceException);
+ assertTrue(cause.getMessage().startsWith(
+ "Error in module 'baz': cannot add '(baz?revision=2018-02-28)bar'. Node name collision: "));
+ }
+ }
+}
--- /dev/null
+module bar {
+ yang-version 1.1;
+ namespace bar;
+ prefix bar;
+
+ revision "2018-02-11";
+
+ grouping foo {
+ container bar {
+
+ }
+ }
+
+ container cont {
+ choice bar {
+ case foo {
+ uses foo;
+ }
+ }
+ }
+
+ augment "/cont" {
+ choice foo {
+ case foo {
+ uses foo;
+ }
+ }
+ }
+}
--- /dev/null
+module baz {
+ yang-version 1.1;
+ namespace baz;
+ prefix baz;
+
+ revision "2018-02-28";
+
+ grouping foo {
+ container bar {
+
+ }
+ }
+
+ container cont {
+ choice bar {
+ case foo {
+ uses foo;
+ }
+ }
+
+ container bar {
+
+ }
+ }
+}
--- /dev/null
+module foo {
+ yang-version 1.1;
+ namespace foo;
+ prefix foo;
+
+ revision "2018-02-11";
+
+ grouping foo {
+ container bar {
+
+ }
+ }
+
+ container cont {
+ choice bar {
+ case foo {
+ uses foo;
+ }
+ }
+ }
+
+ augment "/cont/bar" {
+ case foo2 {
+ uses foo;
+ }
+ }
+}