BUG-5717: add unique child identifier 41/48241/11
authorRobert Varga <rovarga@cisco.com>
Fri, 11 Nov 2016 02:46:54 +0000 (03:46 +0100)
committerRobert Varga <nite@hq.sk>
Fri, 18 Nov 2016 09:52:24 +0000 (09:52 +0000)
We need to synchronize the parse tree walker with
the StatementWriter, so they can pair statements
across multiple phases. Adjust StatementWriter
to take an int childId to achieve this.

Change-Id: Ie3e210a9084cbe5a86212046083c359393a912f8
Signed-off-by: Robert Varga <rovarga@cisco.com>
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/impl/YangStatementParserListenerImpl.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/rfc6020/repo/YinStatementStreamSource.java
yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/spi/source/StatementWriter.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 [new file with mode: 0644]

index c9bd2c61a499ad76d73b6fb23967ea197785be46..45bde58754e4ea9bf0f332a92bfff67f1a702711 100644 (file)
@@ -8,7 +8,9 @@
 package org.opendaylight.yangtools.yang.parser.impl;
 
 import com.google.common.base.Verify;
+import java.util.ArrayDeque;
 import java.util.ArrayList;
+import java.util.Deque;
 import java.util.List;
 import javax.annotation.concurrent.Immutable;
 import org.opendaylight.yangtools.antlrv4.code.gen.YangStatementParser.ArgumentContext;
@@ -28,7 +30,16 @@ import org.opendaylight.yangtools.yang.parser.stmt.rfc6020.Utils;
 
 @Immutable
 public class YangStatementParserListenerImpl extends YangStatementParserBaseListener {
+    private static final class Counter {
+        private int value = 0;
+
+        int getAndIncrement() {
+            return value++;
+        }
+    }
+
     private final List<String> toBeSkipped = new ArrayList<>();
+    private final Deque<Counter> counters = new ArrayDeque<>();
     private final String sourceName;
     private QNameToStatementDefinition stmtDef;
     private PrefixToModule prefixes;
@@ -41,12 +52,20 @@ public class YangStatementParserListenerImpl extends YangStatementParserBaseList
     public void setAttributes(final StatementWriter writer, final QNameToStatementDefinition stmtDef) {
         this.writer = writer;
         this.stmtDef = stmtDef;
+        initCounters();
     }
 
-    public void setAttributes(final StatementWriter writer, final QNameToStatementDefinition stmtDef, final PrefixToModule prefixes) {
+    public void setAttributes(final StatementWriter writer, final QNameToStatementDefinition stmtDef,
+            final PrefixToModule prefixes) {
         this.writer = writer;
         this.stmtDef = stmtDef;
         this.prefixes = prefixes;
+        initCounters();
+    }
+
+    private void initCounters() {
+        counters.clear();
+        counters.push(new Counter());
     }
 
     @Override
@@ -57,6 +76,8 @@ public class YangStatementParserListenerImpl extends YangStatementParserBaseList
         final QName identifier = QName.create(YangConstants.RFC6020_YIN_MODULE, keywordTxt);
         final QName validStatementDefinition = Utils.getValidStatementDefinition(prefixes, stmtDef, identifier);
 
+        final int childId = counters.peek().getAndIncrement();
+        counters.push(new Counter());
         if (stmtDef == null || validStatementDefinition == null || !toBeSkipped.isEmpty()) {
             SourceException.throwIf(writer.getPhase() == ModelProcessingPhase.FULL_DECLARATION, ref,
                     "%s is not a YANG statement or use of extension.", keywordTxt);
@@ -66,7 +87,7 @@ public class YangStatementParserListenerImpl extends YangStatementParserBaseList
 
         final ArgumentContext argumentCtx = ctx.getChild(ArgumentContext.class, 0);
         final String argument = argumentCtx != null ? Utils.stringFromStringContext(argumentCtx) : null;
-        writer.startStatement(validStatementDefinition, argument, ref);
+        writer.startStatement(childId, validStatementDefinition, argument, ref);
     }
 
     @Override
@@ -84,5 +105,6 @@ public class YangStatementParserListenerImpl extends YangStatementParserBaseList
 
         // No-op if the statement is not on the list
         toBeSkipped.remove(statementName);
+        counters.pop();
     }
 }
index e162e801ba5a280c016381f24934f0eaaf2f8a30..05ceca6219cd5ea874a7b4149a862ed3b1d1ac13 100644 (file)
@@ -87,7 +87,7 @@ public final class YinStatementStreamSource implements Identifiable<SourceIdenti
         return def;
     }
 
-    private static void processAttribute(final Attr attr, final StatementWriter writer,
+    private static void processAttribute(final int childId, final Attr attr, final StatementWriter writer,
             final QNameToStatementDefinition stmtDef, final StatementSourceReference ref) {
         final StatementDefinition def = getValidDefinition(attr, writer, stmtDef, ref);
         if (def == null) {
@@ -95,7 +95,7 @@ public final class YinStatementStreamSource implements Identifiable<SourceIdenti
         }
 
         final String value = attr.getValue();
-        writer.startStatement(def.getStatementName(), value.isEmpty() ? null : value, ref);
+        writer.startStatement(childId, def.getStatementName(), value.isEmpty() ? null : value, ref);
         writer.endStatement(ref);
     }
 
@@ -117,7 +117,7 @@ public final class YinStatementStreamSource implements Identifiable<SourceIdenti
         return attr.getValue();
     }
 
-    private static void processElement(final Element element, final StatementWriter writer,
+    private static void processElement(final int childId, final Element element, final StatementWriter writer,
             final QNameToStatementDefinition stmtDef) {
         final StatementSourceReference ref = extractRef(element);
         final StatementDefinition def = getValidDefinition(element, writer, stmtDef, ref);
@@ -143,7 +143,10 @@ public final class YinStatementStreamSource implements Identifiable<SourceIdenti
             allElements = false;
         }
 
-        writer.startStatement(def.getStatementName(), argValue, ref);
+        writer.startStatement(childId, def.getStatementName(), argValue, ref);
+
+        // Child counter
+        int childCounter = 0;
 
         // First process any statements defined as attributes. We need to skip argument, if present
         final NamedNodeMap attributes = element.getAttributes();
@@ -151,7 +154,7 @@ public final class YinStatementStreamSource implements Identifiable<SourceIdenti
             for (int i = 0, len = attributes.getLength(); i < len; ++i) {
                 final Attr attr = (Attr) attributes.item(i);
                 if (allAttrs || !isArgument(argName, attr)) {
-                    processAttribute(attr, writer, stmtDef, ref);
+                    processAttribute(childCounter++, attr, writer, stmtDef, ref);
                 }
             }
         }
@@ -162,7 +165,7 @@ public final class YinStatementStreamSource implements Identifiable<SourceIdenti
             final Node child = children.item(i);
             if (child.getNodeType() == Node.ELEMENT_NODE) {
                 if (allElements || !isArgument(argName, child)) {
-                    processElement((Element) child, writer, stmtDef);
+                    processElement(childCounter++, (Element) child, writer, stmtDef);
                 }
             }
         }
@@ -176,10 +179,12 @@ public final class YinStatementStreamSource implements Identifiable<SourceIdenti
 
     private void walkTree(final StatementWriter writer, final QNameToStatementDefinition stmtDef) {
         final NodeList children = root.getChildNodes();
+
+        int childCounter = 0;
         for (int i = 0, len = children.getLength(); i < len; ++i) {
             final Node child = children.item(i);
             if (child.getNodeType() == Node.ELEMENT_NODE) {
-                processElement((Element) child, writer, stmtDef);
+                processElement(childCounter++, (Element) child, writer, stmtDef);
             }
         }
     }
index 62f9bb2c3cb2aeaff4b90a31c8ff99218312eda5..d87f4d7444605ee801260c1b8c8fad12d5ef1832 100644 (file)
@@ -26,9 +26,12 @@ public interface StatementWriter {
      * </p>
      * <p>
      * If statement has substatements, in order to start substatement, call to
-     * {@link #startStatement(QName, String, StatementSourceReference)} needs to be done
+     * {@link #startStatement(int, QName, String, StatementSourceReference)} needs to be done
      * for substatement.
      *
+     * @param childId
+     *            Child identifier, unique among siblings
+     *
      * @param name
      *            Fully qualified name of statement.
      * @param argument
@@ -39,7 +42,8 @@ public interface StatementWriter {
      * @throws SourceException
      *             if statement is not valid according to current context.
      */
-    void startStatement(@Nonnull QName name, @Nullable String argument, @Nonnull StatementSourceReference ref);
+    void startStatement(final int childId, @Nonnull QName name, @Nullable String argument,
+            @Nonnull StatementSourceReference ref);
 
     /**
      * Ends current opened statement.
index 21499195a84d3c31e289e2262250648f2396ca0d..7b30b53f3932d434a7a34ae22e9027e3a543c485 100644 (file)
@@ -135,8 +135,8 @@ public class SourceSpecificContext implements NamespaceStorageNode, NamespaceBeh
         return inProgressPhase;
     }
 
-    ContextBuilder<?, ?, ?> createDeclaredChild(final StatementContextBase<?, ?, ?> current, QName name,
-                                                final String argument, final StatementSourceReference ref) {
+    ContextBuilder<?, ?, ?> createDeclaredChild(final StatementContextBase<?, ?, ?> current, final int childId,
+            QName name, final String argument, final StatementSourceReference ref) {
         // FIXME: BUG-7038: Refactor/clean up this special case
         if (TYPE.equals(name)) {
             SourceException.throwIfNull(argument, ref, "Type statement requires an argument");
@@ -176,7 +176,7 @@ public class SourceSpecificContext implements NamespaceStorageNode, NamespaceBeh
         if (current == null) {
             ret = new RootContextBuilder(def, ref);
         } else {
-            ret = current.substatementBuilder(def, ref);
+            ret = current.substatementBuilder(childId, def, ref);
         }
 
         if (argument != null) {
index 9d27131c64a26710eefd083242c6d90e6332af40..d51806909885cac52e5dd20ae7a539116e0816c2 100644 (file)
@@ -13,7 +13,6 @@ import com.google.common.base.Preconditions;
 import com.google.common.base.Throwables;
 import com.google.common.collect.ImmutableCollection;
 import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.ImmutableMultimap;
 import com.google.common.collect.Multimap;
 import com.google.common.collect.Multimaps;
@@ -22,12 +21,9 @@ import java.util.Collection;
 import java.util.Collections;
 import java.util.EnumMap;
 import java.util.EventListener;
-import java.util.HashMap;
 import java.util.Iterator;
-import java.util.Map;
 import javax.annotation.Nonnull;
 import org.opendaylight.yangtools.concepts.Identifiable;
-import org.opendaylight.yangtools.yang.model.api.Rfc6020Mapping;
 import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
 import org.opendaylight.yangtools.yang.model.api.meta.EffectiveStatement;
 import org.opendaylight.yangtools.yang.model.api.meta.IdentifierNamespace;
@@ -51,27 +47,20 @@ public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E
 
     @SuppressWarnings({ "rawtypes", "unchecked" })
     private final class SubContextBuilder extends ContextBuilder {
-        SubContextBuilder(final StatementDefinitionContext def, final StatementSourceReference sourceRef) {
+        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 = null;
-
-            final StatementDefinition stmtDef = getDefinition().getPublicView();
-            // FIXME: this is rather ugly. Rather than having an explicit blacklist, StatementDefinitionContext should
-            //        give us information whether we should really bother with the substatements map.
-            if (stmtDef != Rfc6020Mapping.AUGMENT && stmtDef != Rfc6020Mapping.DEVIATION
-                    && stmtDef != Rfc6020Mapping.IMPORT && stmtDef != Rfc6020Mapping.TYPE) {
-                potential = substatements.get(createIdentifier());
-            }
+            StatementContextBase<?, ?, ?> potential = substatements.get(childId);
             if (potential == null) {
                 potential = new SubstatementContext(StatementContextBase.this, this);
-                if (substatements.isEmpty()) {
-                    substatements = new HashMap<>(1);
-                }
-                substatements.put(createIdentifier(), potential);
+                substatements = substatements.put(childId, potential);
                 getDefinition().onStatementAdded(potential);
             }
             potential.resetLists();
@@ -121,10 +110,10 @@ 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 Map<StatementIdentifier, StatementContextBase<?, ?, ?>> substatements = ImmutableMap.of();
     private Collection<StatementContextBase<?, ?, ?>> declared = ImmutableList.of();
     private Collection<StatementContextBase<?, ?, ?>> effective = ImmutableList.of();
     private Collection<StatementContextBase<?, ?, ?>> effectOfStatement = ImmutableList.of();
+    private StatementMap substatements = StatementMap.empty();
 
     private SupportedByFeatures supportedByFeatures = SupportedByFeatures.UNDEFINED;
     private CopyHistory copyHistory = CopyHistory.original();
@@ -403,9 +392,9 @@ public abstract class StatementContextBase<A, D extends DeclaredStatement<A>, E
      *
      * @return instance of ContextBuilder
      */
-    public ContextBuilder<?, ?, ?> substatementBuilder(final StatementDefinitionContext<?, ?, ?> def,
+    ContextBuilder<?, ?, ?> substatementBuilder(final int childId, final StatementDefinitionContext<?, ?, ?> def,
             final StatementSourceReference ref) {
-        return new SubContextBuilder(def, ref);
+        return new SubContextBuilder(childId, def, ref);
     }
 
     /**
index 1e85389c68ffe318b820b58a07879032743d4c27..8a8e4c2caa833f882acd12901196732db5ee189b 100644 (file)
@@ -27,9 +27,10 @@ final class StatementContextWriter implements StatementWriter {
     }
 
     @Override
-    public void startStatement(final QName name, final String argument, final StatementSourceReference ref) {
+    public void startStatement(final int childId, final QName name, final String argument,
+            final StatementSourceReference ref) {
         deferredCreate();
-        current = ctx.createDeclaredChild(parent, name, argument, ref);
+        current = ctx.createDeclaredChild(parent, childId, name, argument, ref);
     }
 
     @Override
diff --git a/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/StatementMap.java b/yang/yang-parser-impl/src/main/java/org/opendaylight/yangtools/yang/parser/stmt/reactor/StatementMap.java
new file mode 100644 (file)
index 0000000..0f28471
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2016 Cisco Systems, Inc. and others.  All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.yangtools.yang.parser.stmt.reactor;
+
+import com.google.common.base.Preconditions;
+import java.util.Arrays;
+import javax.annotation.Nonnull;
+
+/**
+ * Simple integer-to-StatementContextBase map optimized for size and restricted in scope of operations.
+ *
+ * @author Robert Varga
+ */
+abstract class StatementMap {
+    private static final class Empty extends StatementMap {
+        @Override
+        StatementContextBase<?, ?, ?> get(final int index) {
+            return null;
+        }
+
+        @Override
+        StatementMap put(final int index, final StatementContextBase<?, ?, ?> object) {
+            return index == 0 ? new Singleton(object) : new Regular(index, object);
+        }
+    }
+
+    private static final class Regular extends StatementMap {
+        private StatementContextBase<?, ?, ?>[] elements;
+
+        Regular(final int index, final StatementContextBase<?, ?, ?> object) {
+            elements = new StatementContextBase<?, ?, ?>[index + 1];
+            elements[index] = Preconditions.checkNotNull(object);
+        }
+
+        Regular(final StatementContextBase<?, ?, ?> object0, final int index,
+                final StatementContextBase<?, ?, ?> object) {
+            elements = new StatementContextBase<?, ?, ?>[index + 1];
+            elements[0] = Preconditions.checkNotNull(object0);
+            elements[index] = Preconditions.checkNotNull(object);
+        }
+
+        @Override
+        StatementContextBase<?, ?, ?> get(final int index) {
+            if (index >= elements.length) {
+                return null;
+            }
+
+            return elements[index];
+        }
+
+        @Override
+        StatementMap put(final int index, final StatementContextBase<?, ?, ?> object) {
+            if (index < elements.length) {
+                Preconditions.checkArgument(elements[index] == null);
+            } else {
+                elements = Arrays.copyOf(elements, index + 1);
+            }
+
+            elements[index] = Preconditions.checkNotNull(object);
+            return this;
+        }
+    }
+
+    private static final class Singleton extends StatementMap {
+        private final StatementContextBase<?, ?, ?> object;
+
+        Singleton(final StatementContextBase<?, ?, ?> object) {
+            this.object = Preconditions.checkNotNull(object);
+        }
+
+        @Override
+        StatementContextBase<?, ?, ?> get(final int index) {
+            return index == 0 ? object : null;
+        }
+
+        @Override
+        StatementMap put(final int index, final StatementContextBase<?, ?, ?> object) {
+            Preconditions.checkArgument(index != 0);
+            return new Regular(this.object, index, object);
+        }
+    }
+
+    private static final StatementMap EMPTY = new Empty();
+
+    static StatementMap empty() {
+        return EMPTY;
+    }
+
+    abstract StatementContextBase<?, ?, ?> get(int index);
+    abstract @Nonnull StatementMap put(int index, @Nonnull StatementContextBase<?, ?, ?> object);
+}