+ /**
+ * Removes a statement context from the effective substatements
+ * based on its statement definition (i.e statement keyword) and raw (in String form) statement argument.
+ * The statement context is removed only if both statement definition and statement argument match with
+ * one of the effective substatements' statement definition and argument.
+ *
+ * If the statementArg parameter is null, the statement context is removed based only on its statement definition.
+ *
+ * @param statementDef statement definition of the statement context to remove
+ * @param statementArg statement argument of the statement context to remove
+ */
+ public void removeStatementFromEffectiveSubstatements(final StatementDefinition statementDef,
+ final String statementArg) {
+ if (statementArg == null) {
+ removeStatementFromEffectiveSubstatements(statementDef);
+ }
+
+ if (effective.isEmpty()) {
+ return;
+ }
+
+ final Iterator<Mutable<?, ?, ?>> iterator = effective.iterator();
+ while (iterator.hasNext()) {
+ final Mutable<?, ?, ?> next = iterator.next();
+ if (statementDef.equals(next.getPublicDefinition()) && statementArg.equals(next.rawStatementArgument())) {
+ iterator.remove();
+ }
+ }
+
+ shrinkEffective();
+ }
+
+ /**
+ * adds effective statement to collection of substatements
+ *
+ * @param substatement substatement
+ * @throws IllegalStateException
+ * if added in declared phase
+ * @throws NullPointerException
+ * if statement parameter is null
+ */
+ public void addEffectiveSubstatement(final Mutable<?, ?, ?> substatement) {
+ beforeAddEffectiveStatement(1);
+ effective.add(substatement);
+ }
+
+ /**
+ * adds effective statement to collection of substatements
+ *
+ * @param substatements substatements
+ * @throws IllegalStateException
+ * if added in declared phase
+ * @throws NullPointerException
+ * if statement parameter is null
+ */
+ public void addEffectiveSubstatements(final Collection<? extends Mutable<?, ?, ?>> substatements) {
+ if (substatements.isEmpty()) {
+ return;
+ }
+
+ substatements.forEach(Preconditions::checkNotNull);
+ beforeAddEffectiveStatement(substatements.size());
+ effective.addAll(substatements);
+ }
+
+ private void beforeAddEffectiveStatement(final int toAdd) {
+ final ModelProcessingPhase inProgressPhase = getRoot().getSourceContext().getInProgressPhase();
+ Preconditions.checkState(inProgressPhase == ModelProcessingPhase.FULL_DECLARATION
+ || inProgressPhase == ModelProcessingPhase.EFFECTIVE_MODEL,
+ "Effective statement cannot be added in declared phase at: %s", getStatementSourceReference());
+
+ if (effective.isEmpty()) {
+ effective = new ArrayList<>(toAdd);
+ }
+ }
+
+ /**
+ * Create a new substatement at the specified offset.
+ *
+ * @param offset Substatement offset
+ * @param def definition context
+ * @param ref source reference
+ * @param argument statement argument
+ * @return A new substatement
+ */
+ public 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());
+
+ final Optional<StatementContextBase<?, ?, ?>> implicitStatement = definition.beforeSubStatementCreated(this,
+ offset, def, ref, argument);
+ if (implicitStatement.isPresent()) {
+ return implicitStatement.get().createSubstatement(offset, def, ref, argument);
+ }
+
+ final StatementContextBase<CA, CD, CE> ret = new SubstatementContext<>(this, def, ref, argument);
+ substatements = substatements.put(offset, ret);
+ def.onStatementAdded(ret);
+ return ret;
+ }
+
+ /**
+ * Lookup substatement by its offset in this statement.
+ *
+ * @param offset Substatement offset
+ * @return Substatement, or null if substatement does not exist.
+ */
+ final StatementContextBase<?, ?, ?> lookupSubstatement(final int offset) {
+ return substatements.get(offset);