Do not synchronize around ReactorStmtCtx.schemaPath
[yangtools.git] / yang / yang-parser-reactor / src / main / java / org / opendaylight / yangtools / yang / parser / stmt / reactor / ReactorStmtCtx.java
index d8283e8882d0577ffa201b30a01b29153ddbe439..5e9f5983e2f2e697cb465a7798414a659adc17f4 100644 (file)
@@ -7,6 +7,7 @@
  */
 package org.opendaylight.yangtools.yang.parser.stmt.reactor;
 
+import static com.google.common.base.Preconditions.checkArgument;
 import static com.google.common.base.Verify.verify;
 
 import com.google.common.base.MoreObjects;
@@ -19,6 +20,7 @@ import java.util.Set;
 import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.Nullable;
 import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.common.QNameModule;
 import org.opendaylight.yangtools.yang.common.YangVersion;
 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
 import org.opendaylight.yangtools.yang.model.api.meta.DeclaredStatement;
@@ -31,6 +33,7 @@ import org.opendaylight.yangtools.yang.model.api.stmt.RefineStatement;
 import org.opendaylight.yangtools.yang.model.api.stmt.SchemaNodeIdentifier;
 import org.opendaylight.yangtools.yang.model.api.stmt.UsesStatement;
 import org.opendaylight.yangtools.yang.model.repo.api.SourceIdentifier;
+import org.opendaylight.yangtools.yang.parser.spi.meta.CopyType;
 import org.opendaylight.yangtools.yang.parser.spi.meta.EffectiveStmtCtx.Current;
 import org.opendaylight.yangtools.yang.parser.spi.meta.InferenceException;
 import org.opendaylight.yangtools.yang.parser.spi.meta.ModelActionBuilder;
@@ -116,6 +119,13 @@ abstract class ReactorStmtCtx<A, D extends DeclaredStatement<A>, E extends Effec
     //        of this flag -- eliminating the initial alignment shadow used by below gap-filler fields.
     private boolean isSupportedToBuildEffective = true;
 
+    // EffectiveConfig mapping
+    private static final int MASK_CONFIG                = 0x03;
+    private static final int HAVE_CONFIG                = 0x04;
+    // Effective instantiation mechanics for StatementContextBase: if this flag is set all substatements are known not
+    // change when instantiated. This includes context-independent statements as well as any statements which are
+    // ignored during copy instantiation.
+    private static final int ALL_INDEPENDENT            = 0x08;
     // Flag bit assignments
     private static final int IS_SUPPORTED_BY_FEATURES   = 0x10;
     private static final int HAVE_SUPPORTED_BY_FEATURES = 0x20;
@@ -125,9 +135,6 @@ abstract class ReactorStmtCtx<A, D extends DeclaredStatement<A>, E extends Effec
     private static final int SET_SUPPORTED_BY_FEATURES  = HAVE_SUPPORTED_BY_FEATURES | IS_SUPPORTED_BY_FEATURES;
     private static final int SET_IGNORE_IF_FEATURE      = HAVE_IGNORE_IF_FEATURE | IS_IGNORE_IF_FEATURE;
 
-    // EffectiveConfig mapping
-    private static final int MASK_CONFIG = 0x03;
-    private static final int HAVE_CONFIG = 0x04;
     private static final EffectiveConfig[] EFFECTIVE_CONFIGS;
 
     static {
@@ -147,8 +154,8 @@ abstract class ReactorStmtCtx<A, D extends DeclaredStatement<A>, E extends Effec
 
     // SchemaPath cache for use with SubstatementContext and InferredStatementContext. This hurts RootStatementContext
     // a bit in terms of size -- but those are only a few and SchemaPath is on its way out anyway.
-    @Deprecated
-    private volatile SchemaPath schemaPath;
+    // FIXME: this should become 'QName'
+    private SchemaPath schemaPath;
 
     ReactorStmtCtx() {
         // Empty on purpose
@@ -306,6 +313,30 @@ abstract class ReactorStmtCtx<A, D extends DeclaredStatement<A>, E extends Effec
         // definition().onNamespaceElementAdded(this, type, key, value);
     }
 
+    /**
+     * Return the effective statement view of a copy operation. This method may return one of:
+     * <ul>
+     *   <li>{@code this}, when the effective view did not change</li>
+     *   <li>an InferredStatementContext, when there is a need for inference-equivalent copy</li>
+     *   <li>{@code null}, when the statement failed to materialize</li>
+     * </ul>
+     *
+     * @param parent Proposed new parent
+     * @param type Copy operation type
+     * @param targetModule New target module
+     * @return {@link ReactorStmtCtx} holding effective view
+     */
+    abstract @Nullable ReactorStmtCtx<?, ?, ?> asEffectiveChildOf(StatementContextBase<?, ?, ?> parent, CopyType type,
+        QNameModule targetModule);
+
+    @Override
+    public final ReactorStmtCtx<A, D, E> replicaAsChildOf(final Mutable<?, ?, ?> parent) {
+        checkArgument(parent instanceof StatementContextBase, "Unsupported parent %s", parent);
+        return replicaAsChildOf((StatementContextBase<?, ?, ?>) parent);
+    }
+
+    abstract @NonNull ReplicaStatementContext<A, D, E> replicaAsChildOf(@NonNull StatementContextBase<?, ?, ?> parent);
+
     //
     //
     // Statement build entry points -- both public and package-private.
@@ -478,7 +509,7 @@ abstract class ReactorStmtCtx<A, D extends DeclaredStatement<A>, E extends Effec
         return false;
     }
 
-    // These two exists only due to memory optimization, should live in AbstractResumedStatement. We are also reusing
+    // These two exist only due to memory optimization, should live in AbstractResumedStatement. We are also reusing
     // this for ReplicaStatementContext's refcount tracking.
     final boolean fullyDefined() {
         return fullyDefined;
@@ -488,6 +519,37 @@ abstract class ReactorStmtCtx<A, D extends DeclaredStatement<A>, E extends Effec
         fullyDefined = true;
     }
 
+    // These two exist only for StatementContextBase. Since we are squeezed for size, with only a single bit available
+    // in flags, we default to 'false' and only set the flag to true when we are absolutely sure -- and all other cases
+    // err on the side of caution by taking the time to evaluate each substatement separately.
+    final boolean allSubstatementsContextIndependent() {
+        return (flags & ALL_INDEPENDENT) != 0;
+    }
+
+    final void setAllSubstatementsContextIndependent() {
+        flags |= ALL_INDEPENDENT;
+    }
+
+    //
+    //
+    // Various functionality from AbstractTypeStatementSupport. This used to work on top of SchemaPath, now it still
+    // lives here. Ultimate future is either proper graduation or (more likely) move to AbstractTypeStatementSupport.
+    //
+    //
+
+    @Override
+    public final QName argumentAsTypeQName() {
+        final Object argument = argument();
+        verify(argument instanceof String, "Unexpected argument %s", argument);
+        return interpretAsQName((String) argument);
+    }
+
+    @Override
+    public final QNameModule effectiveNamespace() {
+        // FIXME: there has to be a better way to do this
+        return getSchemaPath().getLastComponent().getModule();
+    }
+
     //
     //
     // Common SchemaPath cache. All of this is bound to be removed once YANGTOOLS-1066 is done.
@@ -496,26 +558,17 @@ abstract class ReactorStmtCtx<A, D extends DeclaredStatement<A>, E extends Effec
 
     // Exists only to support {SubstatementContext,InferredStatementContext}.schemaPath()
     @Deprecated
-    final @NonNull Optional<SchemaPath> substatementGetSchemaPath() {
-        SchemaPath local = schemaPath;
-        if (local == null) {
-            synchronized (this) {
-                local = schemaPath;
-                if (local == null) {
-                    schemaPath = local = createSchemaPath((StatementContextBase<?, ?, ?>) coerceParentContext());
-                }
-            }
+    final @Nullable SchemaPath substatementGetSchemaPath() {
+        if (schemaPath == null) {
+            schemaPath = createSchemaPath((StatementContextBase<?, ?, ?>) coerceParentContext());
         }
-
-        return Optional.ofNullable(local);
+        return schemaPath;
     }
 
+    // FIXME: 7.0.0: this method's logic needs to be moved to the respective StatementSupport classes
     @Deprecated
     private SchemaPath createSchemaPath(final StatementContextBase<?, ?, ?> parent) {
-        final Optional<SchemaPath> maybeParentPath = parent.schemaPath();
-        verify(maybeParentPath.isPresent(), "Parent %s does not have a SchemaPath", parent);
-        final SchemaPath parentPath = maybeParentPath.get();
-
+        final SchemaPath parentPath = parent.getSchemaPath();
         if (StmtContextUtils.isUnknownStatement(this)) {
             return parentPath.createChild(publicDefinition().getStatementName());
         }
@@ -523,16 +576,13 @@ abstract class ReactorStmtCtx<A, D extends DeclaredStatement<A>, E extends Effec
         if (argument instanceof QName) {
             final QName qname = (QName) argument;
             if (producesDeclared(UsesStatement.class)) {
-                return maybeParentPath.orElse(null);
+                return parentPath;
             }
 
             return parentPath.createChild(qname);
         }
         if (argument instanceof String) {
-            // FIXME: This may yield illegal argument exceptions
-            final Optional<StmtContext<A, D, E>> originalCtx = getOriginalCtx();
-            final QName qname = StmtContextUtils.qnameFromArgument(originalCtx.orElse(this), (String) argument);
-            return parentPath.createChild(qname);
+            return parentPath.createChild(interpretAsQName((String) argument));
         }
         if (argument instanceof SchemaNodeIdentifier
                 && (producesDeclared(AugmentStatement.class) || producesDeclared(RefineStatement.class)
@@ -541,8 +591,13 @@ abstract class ReactorStmtCtx<A, D extends DeclaredStatement<A>, E extends Effec
             return parentPath.createChild(((SchemaNodeIdentifier) argument).getNodeIdentifiers());
         }
 
-        // FIXME: this does not look right
-        return maybeParentPath.orElse(null);
+        // FIXME: this does not look right, investigate more?
+        return parentPath;
+    }
+
+    private @NonNull QName interpretAsQName(final String argument) {
+        // FIXME: This may yield illegal argument exceptions
+        return StmtContextUtils.qnameFromArgument(getOriginalCtx().orElse(this), argument);
     }
 
     //
@@ -605,6 +660,15 @@ abstract class ReactorStmtCtx<A, D extends DeclaredStatement<A>, E extends Effec
         }
     }
 
+    /**
+     * Return {@code true} if this context has an outstanding reference.
+     *
+     * @return True if this context has an outstanding reference.
+     */
+    final boolean haveRef() {
+        return refcount > REFCOUNT_NONE;
+    }
+
     private void lastDecRef() {
         if (noImplictRef()) {
             // We are no longer guarded by effective instance