@Override
  public Collection<SNode> tryToApply(
      TemplateExecutionEnvironment environment, TemplateContext context)
      throws GenerationException {
    if (!checkCondition(context, environment.getGenerator())) {
      return null;
    }

    environment.getTracer().pushRule(myNodePointer);
    try {
      if (environment.getGenerator().isIncremental()) {
        // turn off tracing
        NodeReadEventsCaster.setNodesReadListener(null);
      }

      return apply(context, environment.getEnvironment(context.getInput(), this));
    } catch (AbandonRuleInputException e) {
      return Collections.emptyList();
    } finally {
      if (environment.getGenerator().isIncremental()) {
        // restore tracing
        NodeReadEventsCaster.removeNodesReadListener();
      }
      environment.getTracer().closeRule(myNodePointer);
    }
  }
 @Override
 public void executeInContext(SNode outputNode, TemplateContext context, PostProcessor processor) {
   try {
     NodeReadEventsCaster.setNodesReadListener(listener);
     wrapped.executeInContext(outputNode, context, processor);
   } finally {
     NodeReadEventsCaster.removeNodesReadListener();
   }
 }
 @Override
 public SNode executeInContext(SNode outputNode, TemplateContext context, NodeMapper mapper) {
   try {
     NodeReadEventsCaster.setNodesReadListener(listener);
     return wrapped.executeInContext(outputNode, context, mapper);
   } finally {
     NodeReadEventsCaster.removeNodesReadListener();
   }
 }
 @Override
 public void executeScript(TemplateMappingScript mappingScript, SModel model)
     throws GenerationFailureException {
   try {
     NodeReadEventsCaster.setNodesReadListener(listener);
     wrapped.executeScript(mappingScript, model);
   } finally {
     NodeReadEventsCaster.removeNodesReadListener();
   }
 }
 @Override
 public boolean evaluate(@NotNull IfMacroCondition condition, @NotNull IfMacroContext context)
     throws GenerationFailureException {
   try {
     NodeReadEventsCaster.setNodesReadListener(listener);
     return wrapped.evaluate(condition, context);
   } finally {
     NodeReadEventsCaster.removeNodesReadListener();
   }
 }
 @Override
 public SNode evaluateInsertQuery(
     SNode inputNode, SNode macroNode, SNode query, @NotNull TemplateContext context) {
   try {
     NodeReadEventsCaster.setNodesReadListener(listener);
     return wrapped.evaluateInsertQuery(inputNode, macroNode, query, context);
   } finally {
     NodeReadEventsCaster.removeNodesReadListener();
   }
 }
 @Override
 public Object evaluateVariableQuery(
     SNode inputNode, SNode query, @NotNull TemplateContext context) {
   try {
     NodeReadEventsCaster.setNodesReadListener(listener);
     return wrapped.evaluateVariableQuery(inputNode, query, context);
   } finally {
     NodeReadEventsCaster.removeNodesReadListener();
   }
 }
 @Override
 public SNode getContextNode(TemplateWeavingRule rule, TemplateContext context)
     throws GenerationFailureException {
   try {
     NodeReadEventsCaster.setNodesReadListener(listener);
     return wrapped.getContextNode(rule, context);
   } finally {
     NodeReadEventsCaster.removeNodesReadListener();
   }
 }
 @Override
 public boolean isApplicable(
     @NotNull TemplateRuleWithCondition rule, @NotNull TemplateContext context)
     throws GenerationFailureException {
   try {
     NodeReadEventsCaster.setNodesReadListener(listener);
     return wrapped.isApplicable(rule, context);
   } finally {
     NodeReadEventsCaster.removeNodesReadListener();
   }
 }
 @Nullable
 @Override
 public Object evaluate(@NotNull PropertyValueQuery query, @NotNull PropertyMacroContext context)
     throws GenerationFailureException {
   try {
     NodeReadEventsCaster.setNodesReadListener(listener);
     return wrapped.evaluate(query, context);
   } finally {
     NodeReadEventsCaster.removeNodesReadListener();
   }
 }
 @Override
 public SNode getContextNodeForTemplateFragment(
     SNode templateFragmentNode, SNode mainContextNode, @NotNull TemplateContext context) {
   try {
     NodeReadEventsCaster.setNodesReadListener(listener);
     return wrapped.getContextNodeForTemplateFragment(
         templateFragmentNode, mainContextNode, context);
   } finally {
     NodeReadEventsCaster.removeNodesReadListener();
   }
 }
 @NotNull
 @Override
 public Collection<SNode> evaluate(
     @NotNull SourceNodesQuery query, @NotNull SourceSubstituteMacroNodesContext context)
     throws GenerationFailureException {
   try {
     NodeReadEventsCaster.setNodesReadListener(listener);
     return wrapped.evaluate(query, context);
   } finally {
     NodeReadEventsCaster.removeNodesReadListener();
   }
 }
 @Override
 public boolean applyRule(
     TemplateWeavingRule rule, TemplateContext context, SNode outputContextNode)
     throws GenerationException {
   // FIXME why there's code above not to listen when interpreted rule is applied? Should I do the
   // same here?
   try {
     NodeReadEventsCaster.setNodesReadListener(listener);
     return wrapped.applyRule(rule, context, outputContextNode);
   } finally {
     NodeReadEventsCaster.removeNodesReadListener();
   }
 }
 @Override
 public Collection<SNode> applyRule(TemplateRootMappingRule rule, TemplateContext context)
     throws GenerationException {
   if (rule instanceof TemplateRootMappingRuleInterpreted) {
     return wrapped.applyRule(rule, context);
   }
   try {
     NodeReadEventsCaster.setNodesReadListener(listener);
     return wrapped.applyRule(rule, context);
   } finally {
     NodeReadEventsCaster.removeNodesReadListener();
   }
 }
 @Override
 public Object getReferentTarget(
     SNode node, SNode outputNode, SNode refMacro, TemplateContext context) {
   try {
     NodeReadEventsCaster.setNodesReadListener(listener);
     Object target = wrapped.getReferentTarget(node, outputNode, refMacro, context);
     if (target instanceof SNode) {
       listener.readNode((SNode) target);
     }
     return target;
   } finally {
     NodeReadEventsCaster.removeNodesReadListener();
   }
 }
 @Override
 public SNode executeMapSrcNodeMacro(
     SNode inputNode,
     SNode mapSrcNodeOrListMacro,
     SNode parentOutputNode,
     @NotNull TemplateContext context)
     throws GenerationFailureException {
   try {
     NodeReadEventsCaster.setNodesReadListener(listener);
     return wrapped.executeMapSrcNodeMacro(
         inputNode, mapSrcNodeOrListMacro, parentOutputNode, context);
   } finally {
     NodeReadEventsCaster.removeNodesReadListener();
   }
 }
 @Override
 public void executeMapSrcNodeMacro_PostProc(
     SNode inputNode,
     SNode mapSrcNodeOrListMacro,
     SNode outputNode,
     @NotNull TemplateContext context)
     throws GenerationFailureException {
   try {
     NodeReadEventsCaster.setNodesReadListener(listener);
     wrapped.executeMapSrcNodeMacro_PostProc(
         inputNode, mapSrcNodeOrListMacro, outputNode, context);
   } finally {
     NodeReadEventsCaster.removeNodesReadListener();
   }
 }
  public <Result> Result runCheckingAction(_FunctionTypes._return_P0_E0<? extends Result> action) {
    final Set<SNode> accessedNodes = new HashSet<SNode>();
    final Object[] result = new Object[1];
    try {
      AbstractNodesReadListener listener =
          new AbstractNodesReadListener() {
            @Override
            public void nodeUnclassifiedReadAccess(SNode node) {
              SetSequence.fromSet(accessedNodes).addElement(node);
            }

            @Override
            public void nodePropertyReadAccess(SNode node, String name, String value) {
              SetSequence.fromSet(accessedNodes).addElement(node);
            }

            @Override
            public void nodeReferentReadAccess(SNode node, String role, SNode referent) {
              SetSequence.fromSet(accessedNodes).addElement(node);
              SetSequence.fromSet(accessedNodes).addElement(referent);
            }

            @Override
            public void nodeChildReadAccess(SNode node, String role, SNode child) {
              SetSequence.fromSet(accessedNodes).addElement(node);
              SetSequence.fromSet(accessedNodes).addElement(child);
            }
          };
      NodeReadEventsCaster.setNodesReadListener(listener);
      result[0] = action.invoke();
    } finally {
      NodeReadEventsCaster.removeNodesReadListener();
    }
    for (SNode accessedNode : accessedNodes) {
      addDependency(accessedNode);
    }
    return (Result) result[0];
  }
 @Override
 public Collection<SNode> applyRule(TemplateReductionRule rule, TemplateContext context)
     throws GenerationException {
   try {
     final DependenciesReadListener l;
     if (context.getEnvironment().getGenerator().isIncremental()) {
       // this code used to be in TemplateReductionRuleInterpreted, added to address MPS-16916
       // Moved here for next reasons: (a) generated rules shall behave the same as interpreted;
       // (b) this class is the only place we install listeners via NodeReadEventsCaster, and there
       // shall be no way to get into TRRI with a
       // listener installed (for TRRI to uninstall one) other than through this method.
       // However, I don't understand why there's difference in change tracking in incremental vs
       // non-incremental mode,
       // and why only reduction rules are considered.
       l = null; // turn tracing off
     } else {
       l = listener;
     }
     NodeReadEventsCaster.setNodesReadListener(l);
     return wrapped.applyRule(rule, context);
   } finally {
     NodeReadEventsCaster.removeNodesReadListener();
   }
 }