BUG-5222: Reuse substatements across phases
[yangtools.git] / yang / yang-parser-impl / src / main / java / org / opendaylight / yangtools / yang / parser / stmt / reactor / SourceSpecificContext.java
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() {