BUG-5222: Reuse substatements across phases 13/49813/14
authorRobert Varga <rovarga@cisco.com>
Mon, 26 Dec 2016 14:35:49 +0000 (15:35 +0100)
committerRobert Varga <nite@hq.sk>
Mon, 23 Jan 2017 16:40:17 +0000 (16:40 +0000)
Now that we can address substatements accurately, we can reuse
the same statements instead of throwing them out and re-instantiating
them. We still need to switch the root if the YangVersion changes,
as the statement support objects need to be reemitted.

This removes the need for ContextBuilder and its subclasses, simply
because all required information is available when createDeclaredChild()
is called, bringing further simplification to StatementContextWriter.

Once that indirection is removed, the code flow is improved, which
flushed out a failure to validate when an extension requires an arugment,
hence the test models need update to pass validation.

A final effect of these changes is that StatementContextBase.declared
becomes superfluous, as it really is just a view over declared
substatements, which allows us to eliminate that field and instead
project a view of StatementMap's values.

Change-Id: I6fd3e2e2de41305457958673b77a72073215940e
Signed-off-by: Robert Varga <rovarga@cisco.com>
14 files changed:
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/ContextBuilder.java [deleted file]
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/RootStatementContext.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/SourceSpecificContext.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/StatementContextBase.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/StatementContextWriter.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/StatementMap.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/SubstatementContext.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/rfc6020/ModelDefinedStatementDefinition.java
yang/yang-parser-impl/src/test/resources/augment-test/augment-in-augment/baz.yang
yang/yang-parser-impl/src/test/resources/bugs/bug3859/bug3859.yang
yang/yang-parser-impl/src/test/resources/model-new/baz.yang
yang/yang-parser-impl/src/test/resources/model/baz.yang
yang/yang-parser-impl/src/test/resources/rfc7950/model/baz.yang
yang/yang-parser-impl/src/test/resources/semantic-statement-parser/model/baz.yang

diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/ContextBuilder.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/ContextBuilder.java
deleted file mode 100644 (file)
index 1d45dbc..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright (c) 2015 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.parser.stmt.reactor;
-
-import com.google.common.base.Preconditions;
-import javax.annotation.Nonnull;
-import org.opendaylight.yangtools.concepts.Builder;
-import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
-import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
-import org.opendaylight.yangtools.yang.parser.spi.source.SourceException;
-import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference;
-
-abstract class ContextBuilder<A, D extends DeclaredStatement<A>, E extends EffectiveStatement<A, D>>
-    implements Builder<StatementContextBase<A, D, E>> {
-
-    private final StatementDefinitionContext<A, D, E> definition;
-    private final StatementSourceReference stmtRef;
-
-    private StatementSourceReference argRef;
-    private String rawArg;
-
-    ContextBuilder(final StatementDefinitionContext<A, D, E> def, final StatementSourceReference sourceRef) {
-        this.definition = Preconditions.checkNotNull(def);
-        this.stmtRef = Preconditions.checkNotNull(sourceRef);
-    }
-
-    void setArgument(@Nonnull final String argument, @Nonnull final StatementSourceReference argumentSource) {
-        SourceException.throwIf(!definition.hasArgument(), argumentSource, "Statement %s does not take argument",
-            definition.getStatementName());
-        this.rawArg = Preconditions.checkNotNull(argument);
-        this.argRef = Preconditions.checkNotNull(argumentSource);
-    }
-
-    String getRawArgument() {
-        return rawArg;
-    }
-
-    StatementSourceReference getStamementSource() {
-        return stmtRef;
-    }
-
-    StatementSourceReference getArgumentSource() {
-        return argRef;
-    }
-
-    StatementDefinitionContext<A, D, E> getDefinition() {
-        return definition;
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * @throws SourceException when a source-level problem is found
-     */
-    @Override
-    public abstract StatementContextBase<A, D, E> build();
-}
index f17a856f9ba3a18f739485e76a33802347f1ebf5..7417165d8691d9e0f2280a38c6f44f72f2e01844 100644 (file)
@@ -29,6 +29,7 @@ import org.opendaylight.yangtools.yang.parser.spi.meta.NamespaceBehaviour.Storag
 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
 import org.opendaylight.yangtools.yang.parser.spi.source.IncludedModuleContext;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference;
 
 /**
  * Root statement class for a YANG source. All statements defined in that YANG source are mapped underneath an instance
@@ -49,15 +50,16 @@ public class RootStatementContext<A, D extends DeclaredStatement<A>, E extends E
      */
     private Collection<RootStatementContext<?, ?, ?>> includedContexts = ImmutableList.of();
 
-    RootStatementContext(final ContextBuilder<A, D, E> builder, final SourceSpecificContext sourceContext) {
-        super(builder);
+    RootStatementContext(final SourceSpecificContext sourceContext, final StatementDefinitionContext<A, D, E> def,
+        final StatementSourceReference ref, final String rawArgument) {
+        super(def, ref, rawArgument);
         this.sourceContext = Preconditions.checkNotNull(sourceContext);
-        this.argument = builder.getDefinition().parseArgumentValue(this, builder.getRawArgument());
+        this.argument = def.parseArgumentValue(this, rawArgument);
     }
 
-    RootStatementContext(final ContextBuilder<A, D, E> builder, final SourceSpecificContext sourceContext,
-            final YangVersion version) {
-        this(builder, sourceContext);
+    RootStatementContext(final SourceSpecificContext sourceContext, final StatementDefinitionContext<A, D, E> def,
+        final StatementSourceReference ref, final String rawArgument, final YangVersion version) {
+        this(sourceContext, def, ref, rawArgument);
         this.setRootVersion(version);
     }
 
index dadc28d952e0a8920f7e3bc64cf5ddc4cbb8ec7e..3206e0020f16a1ccb96ede5cb6bbce7dac2ae4f2 100644 (file)
@@ -79,36 +79,6 @@ public class SourceSpecificContext implements NamespaceStorageNode, NamespaceBeh
         FINISHED
     }
 
-    @SuppressWarnings({"rawtypes", "unchecked"})
-    private final class RootContextBuilder extends ContextBuilder {
-        RootContextBuilder(final StatementDefinitionContext def, final StatementSourceReference sourceRef) {
-            super(def, sourceRef);
-        }
-
-        @Override
-        public StatementContextBase build() {
-            /*
-             * If root is null or root version is other than default,
-             * we need to create new root.
-             */
-            if (root == null) {
-                root = new RootStatementContext(this, SourceSpecificContext.this);
-            } else if (!RootStatementContext.DEFAULT_VERSION.equals(root.getRootVersion())
-                    && inProgressPhase == ModelProcessingPhase.SOURCE_LINKAGE) {
-                root = new RootStatementContext(this, SourceSpecificContext.this, root.getRootVersion());
-            } else {
-                final QName rootStatement = root.definition().getStatementName();
-                final String rootArgument = root.rawStatementArgument();
-
-                Preconditions.checkState(Objects.equals(getDefinition().getStatementName(), rootStatement)
-                    && Objects.equals(getRawArgument(), rootArgument),
-                    "Root statement was already defined as '%s %s'.", rootStatement, rootArgument);
-            }
-            root.resetLists();
-            return root;
-        }
-    }
-
     private static final Logger LOG = LoggerFactory.getLogger(SourceSpecificContext.class);
     private static final Table<YangVersion, String, StatementSupport<?, ?, ?>> BUILTIN_TYPE_SUPPORTS =
             ImmutableTable.<YangVersion, String, StatementSupport<?, ?, ?>>builder()
@@ -153,8 +123,16 @@ public class SourceSpecificContext implements NamespaceStorageNode, NamespaceBeh
         return inProgressPhase;
     }
 
-    ContextBuilder<?, ?, ?> createDeclaredChild(final StatementContextBase<?, ?, ?> current, final int childId,
+    StatementContextBase<?, ?, ?> createDeclaredChild(final StatementContextBase<?, ?, ?> current, final int childId,
             QName name, final String argument, final StatementSourceReference ref) {
+        if (current != null) {
+            // Fast path: we are entering a statement which was emitted in previous phase
+            final StatementContextBase<?, ?, ?> existing = current.lookupSubstatement(childId);
+            if (existing != null) {
+                return existing;
+            }
+        }
+
         // FIXME: BUG-7038: Refactor/clean up this special case
         if (TYPE.equals(name)) {
             SourceException.throwIfNull(argument, ref, "Type statement requires an argument");
@@ -166,7 +144,6 @@ public class SourceSpecificContext implements NamespaceStorageNode, NamespaceBeh
         }
 
         StatementDefinitionContext<?, ?, ?> def = currentContext.getStatementDefinition(getRootVersion(), name);
-
         if (def == null) {
             final StatementSupport<?, ?, ?> extension = qNameToStmtDefMap.get(name);
             if (extension != null) {
@@ -186,22 +163,38 @@ public class SourceSpecificContext implements NamespaceStorageNode, NamespaceBeh
              */
             final QName qName = Utils.qNameFromArgument(current, name.getLocalName());
             def = new StatementDefinitionContext<>(new UnknownStatementImpl.Definition(
-                new ModelDefinedStatementDefinition(qName)));
+                new ModelDefinedStatementDefinition(qName, argument != null)));
         }
 
-        Preconditions.checkArgument(def != null, "Statement %s does not have type mapping defined.", name);
-        final ContextBuilder<?, ?, ?> ret;
-        if (current == null) {
-            ret = new RootContextBuilder(def, ref);
+        InferenceException.throwIfNull(def, ref, "Statement %s does not have type mapping defined.", name);
+        if (def.hasArgument()) {
+            SourceException.throwIfNull(argument, ref, "Statement %s requires an argument", name);
         } else {
-            ret = current.substatementBuilder(childId, def, ref);
+            SourceException.throwIf(argument != null, ref, "Statement %s does not take argument", name);
         }
 
-        if (argument != null) {
-            ret.setArgument(argument, ref);
+        if (current != null) {
+            return current.createSubstatement(childId, def, ref, argument);
         }
 
-        return ret;
+        /*
+         * If root is null or root version is other than default,
+         * we need to create new root.
+         */
+        if (root == null) {
+            root = new RootStatementContext<>(this, def, ref, argument);
+        } else if (!RootStatementContext.DEFAULT_VERSION.equals(root.getRootVersion())
+                && inProgressPhase == ModelProcessingPhase.SOURCE_LINKAGE) {
+            root = new RootStatementContext<>(this, def, ref, argument, root.getRootVersion());
+        } else {
+            final QName rootStatement = root.definition().getStatementName();
+            final String rootArgument = root.rawStatementArgument();
+
+            Preconditions.checkState(Objects.equals(def.getStatementName(), rootStatement)
+                && Objects.equals(argument, rootArgument),
+                "Root statement was already defined as '%s %s'.", rootStatement, rootArgument);
+        }
+        return root;
     }
 
     RootStatementContext<?, ?, ?> getRoot() {
index c2e42efa594c4f31a7553e06fedd80304383bb84..4a80059f6d46d2d14ccdd15b08a8059df7f48a15 100644 (file)
@@ -43,37 +43,6 @@ import org.opendaylight.yangtools.yang.parser.stmt.reactor.NamespaceBehaviourWit
 public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E extends EffectiveStatement<A, D>>
         extends NamespaceStorageSupport implements StmtContext.Mutable<A, D, E> {
 
-    @SuppressWarnings({ "rawtypes", "unchecked" })
-    private final class SubContextBuilder extends ContextBuilder {
-        final int childId;
-
-        SubContextBuilder(final int childId, final StatementDefinitionContext def,
-            final StatementSourceReference sourceRef) {
-            super(def, sourceRef);
-            this.childId = childId;
-        }
-
-        @Override
-        public StatementContextBase build() {
-            StatementContextBase<?, ?, ?> potential = substatements.get(childId);
-            if (potential == null) {
-                potential = new SubstatementContext(StatementContextBase.this, this);
-                substatements = substatements.put(childId, potential);
-                getDefinition().onStatementAdded(potential);
-            }
-            potential.resetLists();
-            switch (this.getStamementSource().getStatementSource()) {
-            case DECLARATION:
-                addDeclaredSubstatement(potential);
-                break;
-            case CONTEXT:
-                addEffectiveSubstatement(potential);
-                break;
-            }
-            return potential;
-        }
-    }
-
     /**
      * event listener when an item is added to model namespace
      */
@@ -108,7 +77,6 @@ public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E
 
     private Multimap<ModelProcessingPhase, OnPhaseFinished> phaseListeners = ImmutableMultimap.of();
     private Multimap<ModelProcessingPhase, ContextMutation> phaseMutation = ImmutableMultimap.of();
-    private Collection<StatementContextBase<?, ?, ?>> declared = ImmutableList.of();
     private Collection<StatementContextBase<?, ?, ?>> effective = ImmutableList.of();
     private Collection<StatementContextBase<?, ?, ?>> effectOfStatement = ImmutableList.of();
     private StatementMap substatements = StatementMap.empty();
@@ -122,10 +90,11 @@ public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E
     private E effectiveInstance;
     private int order = 0;
 
-    StatementContextBase(@Nonnull final ContextBuilder<A, D, E> builder) {
-        this.definition = builder.getDefinition();
-        this.statementDeclSource = builder.getStamementSource();
-        this.rawArgument = builder.getRawArgument();
+    StatementContextBase(final StatementDefinitionContext<A, D, E> def, final StatementSourceReference ref,
+            final String rawArgument) {
+        this.definition = Preconditions.checkNotNull(def);
+        this.statementDeclSource = Preconditions.checkNotNull(ref);
+        this.rawArgument = rawArgument;
     }
 
     StatementContextBase(final StatementContextBase<A, D, E> original) {
@@ -251,28 +220,24 @@ public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E
     }
 
     @Override
-    public String rawStatementArgument() {
+    public final String rawStatementArgument() {
         return rawArgument;
     }
 
-    private static final <T> Collection<T> maybeWrap(final Collection<T> input) {
-        if (input instanceof ImmutableCollection) {
-            return input;
-        }
-
-        return Collections.unmodifiableCollection(input);
-    }
-
     @Nonnull
     @Override
     public Collection<StatementContextBase<?, ?, ?>> declaredSubstatements() {
-        return maybeWrap(declared);
+        return substatements.values();
     }
 
     @Nonnull
     @Override
     public Collection<StatementContextBase<?, ?, ?>> effectiveSubstatements() {
-        return maybeWrap(effective);
+        if (effective instanceof ImmutableCollection) {
+            return effective;
+        }
+
+        return Collections.unmodifiableCollection(effective);
     }
 
     public void removeStatementsFromEffectiveSubstatements(final Collection<StatementContextBase<?, ?, ?>> substatements) {
@@ -351,38 +316,35 @@ public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E
     }
 
     /**
-     * adds declared statement to collection of substatements
+     * Create a new substatement at the specified offset.
      *
-     * @param substatement substatement
-     * @throws IllegalStateException
-     *             if added in effective phase
-     * @throws NullPointerException
-     *             if statement parameter is null
+     * @param offset Substatement offset
+     * @param def definition context
+     * @param ref source reference
+     * @param argument statement argument
+     * @return A new substatement
      */
-    public void addDeclaredSubstatement(final StatementContextBase<?, ?, ?> substatement) {
-
+    final <CA, CD extends DeclaredStatement<CA>, CE extends EffectiveStatement<CA, CD>> StatementContextBase<CA, CD, CE>
+            createSubstatement(final int offset, final StatementDefinitionContext<CA, CD, CE> def,
+                    final StatementSourceReference ref, final String argument) {
         final ModelProcessingPhase inProgressPhase = getRoot().getSourceContext().getInProgressPhase();
         Preconditions.checkState(inProgressPhase != ModelProcessingPhase.EFFECTIVE_MODEL,
                 "Declared statement cannot be added in effective phase at: %s", getStatementSourceReference());
 
-        if (declared.isEmpty()) {
-            declared = new ArrayList<>(1);
-        }
-        declared.add(Preconditions.checkNotNull(substatement,
-                "StatementContextBase declared substatement cannot be null at: %s", getStatementSourceReference()));
+        final StatementContextBase<CA, CD, CE> ret = new SubstatementContext<>(this, def, ref, argument);
+        substatements = substatements.put(offset, ret);
+        def.onStatementAdded(ret);
+        return ret;
     }
 
     /**
-     * builds a new substatement from statement definition context and statement source reference
+     * Lookup substatement by its offset in this statement.
      *
-     * @param def definition context
-     * @param ref source reference
-     *
-     * @return instance of ContextBuilder
+     * @param offset Substatement offset
+     * @return Substatement, or null if substatement does not exist.
      */
-    ContextBuilder<?, ?, ?> substatementBuilder(final int childId, final StatementDefinitionContext<?, ?, ?> def,
-            final StatementSourceReference ref) {
-        return new SubContextBuilder(childId, def, ref);
+    final StatementContextBase<?, ?, ?> lookupSubstatement(final int offset) {
+        return substatements.get(offset);
     }
 
     /**
@@ -409,21 +371,6 @@ public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E
         return effectiveInstance;
     }
 
-    /**
-     * clears collection of declared substatements
-     *
-     * @throws IllegalStateException
-     *             if invoked in effective build phase
-     */
-    void resetLists() {
-
-        final SourceSpecificContext sourceContext = getRoot().getSourceContext();
-        Preconditions.checkState(sourceContext.getInProgressPhase() != ModelProcessingPhase.EFFECTIVE_MODEL,
-                "Declared statements list cannot be cleared in effective phase at: %s", getStatementSourceReference());
-
-        declared = ImmutableList.of();
-    }
-
     /**
      * tries to execute current {@link ModelProcessingPhase} of source parsing
      *
@@ -456,7 +403,7 @@ public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E
             }
         }
 
-        for (final StatementContextBase<?, ?, ?> child : declared) {
+        for (final StatementContextBase<?, ?, ?> child : substatements.values()) {
             finished &= child.tryToCompletePhase(phase);
         }
         for (final StatementContextBase<?, ?, ?> child : effective) {
@@ -471,12 +418,12 @@ public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E
     }
 
     /**
-     * occurs on end of {@link ModelProcessingPhase} of source parsing
+     * Occurs on end of {@link ModelProcessingPhase} of source parsing
      *
      * @param phase
      *            that was to be completed (finished)
      * @throws SourceException
-     *             when an error occured in source parsing
+     *             when an error occurred in source parsing
      */
     private void onPhaseCompleted(final ModelProcessingPhase phase) {
         completedPhase = phase;
index c59d1d94f0ebcaadb9c99e91028887d282a6d931..9b6bda04299aa19355d54b9c4df833525ede1544 100644 (file)
@@ -8,6 +8,7 @@
 package org.opendaylight.yangtools.yang.parser.stmt.reactor;
 
 import com.google.common.base.Preconditions;
+import com.google.common.base.Verify;
 import javax.annotation.Nonnull;
 import org.opendaylight.yangtools.yang.common.QName;
 import org.opendaylight.yangtools.yang.parser.spi.meta.ModelProcessingPhase;
@@ -18,10 +19,9 @@ final class StatementContextWriter implements StatementWriter {
     private final ModelProcessingPhase phase;
     private final SourceSpecificContext ctx;
 
-    private StatementContextBase<?, ?, ?> parent;
-    private ContextBuilder<?, ?, ?> current;
+    private StatementContextBase<?, ?, ?> current;
 
-    public StatementContextWriter(final SourceSpecificContext ctx, final ModelProcessingPhase phase) {
+    StatementContextWriter(final SourceSpecificContext ctx, final ModelProcessingPhase phase) {
         this.ctx = Preconditions.checkNotNull(ctx);
         this.phase = Preconditions.checkNotNull(phase);
     }
@@ -29,16 +29,14 @@ final class StatementContextWriter implements StatementWriter {
     @Override
     public void startStatement(final int childId, @Nonnull final QName name, final String argument,
             @Nonnull final StatementSourceReference ref) {
-        deferredCreate();
-        current = ctx.createDeclaredChild(parent, childId, name, argument, ref);
+        current = Verify.verifyNotNull(ctx.createDeclaredChild(current, childId, name, argument, ref));
     }
 
     @Override
     public void endStatement(@Nonnull final StatementSourceReference ref) {
-        deferredCreate();
-        Preconditions.checkState(parent != null);
-        parent.endDeclared(ref,phase);
-        parent = parent.getParentContext();
+        Preconditions.checkState(current != null);
+        current.endDeclared(ref, phase);
+        current = current.getParentContext();
     }
 
     @Nonnull
@@ -46,11 +44,4 @@ final class StatementContextWriter implements StatementWriter {
     public ModelProcessingPhase getPhase() {
         return phase;
     }
-
-    private void deferredCreate() {
-        if (current != null) {
-            parent = current.build();
-            current = null;
-        }
-    }
 }
index 0f28471fd2c14a9eaf20a88b54e344bfa3176783..246351e35a12f8b6717c92905e6b53907bed10cc 100644 (file)
@@ -8,11 +8,19 @@
 package org.opendaylight.yangtools.yang.parser.stmt.reactor;
 
 import com.google.common.base.Preconditions;
+import com.google.common.collect.AbstractIterator;
+import com.google.common.collect.ImmutableList;
+import java.util.AbstractCollection;
 import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.function.Consumer;
 import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
 
 /**
- * Simple integer-to-StatementContextBase map optimized for size and restricted in scope of operations.
+ * Simple integer-to-StatementContextBase map optimized for size and restricted in scope of operations. It does not
+ * implement {@link java.util.Map} for simplicity's sake.
  *
  * @author Robert Varga
  */
@@ -27,6 +35,11 @@ abstract class StatementMap {
         StatementMap put(final int index, final StatementContextBase<?, ?, ?> object) {
             return index == 0 ? new Singleton(object) : new Regular(index, object);
         }
+
+        @Override
+        Collection<StatementContextBase<?, ?, ?>> values() {
+            return ImmutableList.of();
+        }
     }
 
     private static final class Regular extends StatementMap {
@@ -64,8 +77,69 @@ abstract class StatementMap {
             elements[index] = Preconditions.checkNotNull(object);
             return this;
         }
+
+        @Override
+        Collection<StatementContextBase<?, ?, ?>> values() {
+            return new RegularAsCollection<>(elements);
+        }
+    }
+
+    private static final class RegularAsCollection<T> extends AbstractCollection<T> {
+        private final T[] elements;
+
+        RegularAsCollection(final T[] elements) {
+            this.elements = Preconditions.checkNotNull(elements);
+        }
+
+        @Override
+        public void forEach(final Consumer<? super T> action) {
+            for (T e : elements) {
+                if (e != null) {
+                    action.accept(e);
+                }
+            }
+        }
+
+        @Override
+        public boolean isEmpty() {
+            // This has a single-use and when it is instantiated, we know to have at least two items
+            return false;
+        }
+
+        @Override
+        public Iterator<T> iterator() {
+            return new AbstractIterator<T>() {
+                private int nextOffset = 0;
+
+                @Override
+                protected T computeNext() {
+                    while (nextOffset < elements.length) {
+                        final T ret = elements[nextOffset++];
+                        if (ret != null) {
+                            return ret;
+                        }
+                    }
+
+                    return endOfData();
+                }
+            };
+        }
+
+        @Override
+        public int size() {
+            // Optimized for non-sparse case
+            int nulls = 0;
+            for (T e : elements) {
+                if (e == null) {
+                    nulls++;
+                }
+            }
+
+            return elements.length - nulls;
+        }
     }
 
+
     private static final class Singleton extends StatementMap {
         private final StatementContextBase<?, ?, ?> object;
 
@@ -83,6 +157,11 @@ abstract class StatementMap {
             Preconditions.checkArgument(index != 0);
             return new Regular(this.object, index, object);
         }
+
+        @Override
+        Collection<StatementContextBase<?, ?, ?>> values() {
+            return ImmutableList.of(object);
+        }
     }
 
     private static final StatementMap EMPTY = new Empty();
@@ -91,6 +170,30 @@ abstract class StatementMap {
         return EMPTY;
     }
 
-    abstract StatementContextBase<?, ?, ?> get(int index);
+    /**
+     * Return the statement context at specified index.
+     *
+     * @param index Element index, must be non-negative
+     * @return Requested element or null if there is no element at that index
+     */
+    abstract @Nullable StatementContextBase<?, ?, ?> get(int index);
+
+    /**
+     * Add a statement at specified index.
+     *
+     * @param index Element index, must be non-negative
+     * @param object Object to store
+     * @return New statement map
+     * @throws IllegalArgumentException if the index is already occupied
+     */
     abstract @Nonnull StatementMap put(int index, @Nonnull StatementContextBase<?, ?, ?> object);
+
+    /**
+     * Return a read-only view of the elements in this map. Unlike other maps, this view does not detect concurrent
+     * modification. Iteration is performed in order of increasing offset. In face of concurrent modification, number
+     * of elements returned through iteration may not match the size reported via {@link Collection#size()}.
+     *
+     * @return Read-only view of available statements.
+     */
+    abstract @Nonnull Collection<StatementContextBase<?, ?, ?>> values();
 }
index 3d9d1035955f46d7e0892437bf88abd1d7ae5c36..97557fe531561bb0d20d4c2c912b463f75b70872 100644 (file)
@@ -41,6 +41,7 @@ import org.opendaylight.yangtools.yang.parser.spi.meta.QNameCacheNamespace;
 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContext;
 import org.opendaylight.yangtools.yang.parser.spi.meta.StmtContextUtils;
 import org.opendaylight.yangtools.yang.parser.spi.source.AugmentToChoiceNamespace;
+import org.opendaylight.yangtools.yang.parser.spi.source.StatementSourceReference;
 import org.opendaylight.yangtools.yang.parser.spi.validation.ValidationBundlesNamespace;
 import org.opendaylight.yangtools.yang.parser.spi.validation.ValidationBundlesNamespace.ValidationBundleType;
 import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.Utils;
@@ -67,10 +68,11 @@ final class SubstatementContext<A, D extends DeclaredStatement<A>, E extends Eff
 
     private volatile SchemaPath schemaPath;
 
-    SubstatementContext(final StatementContextBase<?, ?, ?> parent, final ContextBuilder<A, D, E> builder) {
-        super(builder);
+    SubstatementContext(final StatementContextBase<?, ?, ?> parent, final StatementDefinitionContext<A, D, E> def,
+            final StatementSourceReference ref, final String rawArgument) {
+        super(def, ref, rawArgument);
         this.parent = Preconditions.checkNotNull(parent, "Parent must not be null");
-        this.argument = builder.getDefinition().parseArgumentValue(this, builder.getRawArgument());
+        this.argument = def.parseArgumentValue(this, rawArgument);
     }
 
     @SuppressWarnings("unchecked")
index fb36187d84b62c786d9c0ac569ccdb38dd233f6d..4c8827c32f3eab19351a87601d7aeb6dba605297 100644 (file)
@@ -35,8 +35,8 @@ public final class ModelDefinedStatementDefinition implements StatementDefinitio
     }
 
     @Deprecated
-    public ModelDefinedStatementDefinition(final QName qname) {
-        this(qname, qname, false);
+    public ModelDefinedStatementDefinition(final QName qname, final boolean hasArgument) {
+        this(qname, hasArgument ? qname : null, false);
     }
 
     @Nonnull
index f50c46130a5925c63f54021d7a1f58ea2297e989..e7b20a9813bf775b345c5dcf9b468e19f55a0860 100644 (file)
@@ -88,7 +88,7 @@ module baz {
             }
         }
         
-        br:opendaylight;
+        br:opendaylight awesome;
     }
 
 }
index b3d9bc48e0c9f7810b02e41ee3f8c6abb3985b75..1535645aba5e2ed3a907b1813f000b19d300036a 100644 (file)
@@ -6,7 +6,7 @@ module reference-in-unknown {
     argument test;
   }
 
-  riu:test-extension {
+  riu:test-extension test {
     container cont {
       description
         "This is just a plain text";
index a0dcdf846a383d8d7a9f9d08cfef59c5b61207ab..fdff04564ccbc940c56735b385a0ee673ed11582 100644 (file)
@@ -184,6 +184,6 @@ module baz {
             type br:my-decimal-type;
         }
 
-        br:opendaylight;
+        br:opendaylight awesome;
     }
 }
index e88a2be303e34da08db4b7e23650c1613b36c3b2..8b9ded02cb1d9d7ae9a0b3ffdf96bb3bb7183604 100644 (file)
@@ -188,7 +188,7 @@ module baz {
             type br:my-decimal-type;
         }
         
-        br:opendaylight;
+        br:opendaylight awesome;
     }
 
 }
index b7b9c02934075f00619c947c55154e455781ed1c..6cad0a5319da826c60ad905e185ad9bc1287a9e2 100644 (file)
@@ -188,7 +188,7 @@ module baz {
             type br:my-decimal-type;
         }
         
-        br:opendaylight;
+        br:opendaylight awesome;
     }
 
 }
index e88a2be303e34da08db4b7e23650c1613b36c3b2..8b9ded02cb1d9d7ae9a0b3ffdf96bb3bb7183604 100644 (file)
@@ -188,7 +188,7 @@ module baz {
             type br:my-decimal-type;
         }
         
-        br:opendaylight;
+        br:opendaylight awesome;
     }
 
 }