/** Method coolectPatternNodesToBeKeptInfo */
  private void collectPatternNodesToBeKeptInfo() {
    patternNodeIsToBeKept = new int[n_graph_actions][max_n_pattern_nodes];

    // init the arrays with -1
    for (int i = 0; i < n_graph_actions; i++)
      for (int j = 0; j < max_n_pattern_nodes; j++) patternNodeIsToBeKept[i][j] = -1;

    // for all nodes to be kept set the corresponding array entry to the
    // appropriate replacement node number
    for (Rule action : actionRuleMap.keySet()) {
      int act_id = actionRuleMap.get(action).intValue();

      // compute the set of pattern nodes to be kept for this action
      Collection<Node> pattern_nodes_to_keep = new HashSet<Node>();
      pattern_nodes_to_keep.addAll(action.getPattern().getNodes());
      if (action.getRight() != null) {
        Graph replacement = action.getRight();
        pattern_nodes_to_keep.retainAll(replacement.getNodes());
        // iterate over the pattern nodes to be kept and store their
        // corresponding replacement node number
        for (Node node : pattern_nodes_to_keep) {
          int node_num = pattern_node_num.get(act_id).get(node).intValue();
          patternNodeIsToBeKept[act_id][node_num] =
              replacement_node_num.get(act_id).get(node).intValue();
        }
      }
    }
  }
  /** Method collectReplacementNodeIsPreservedNodeInfo */
  private void collectReplacementNodeIsPreservedNodeInfo() {
    replacementNodeIsPreservedNode = new int[n_graph_actions][max_n_replacement_nodes];

    // init the array with -1
    for (int i = 0; i < n_graph_actions; i++)
      for (int j = 0; j < max_n_replacement_nodes; j++) replacementNodeIsPreservedNode[i][j] = -1;

    // for all nodes preserved set the corresponding array entry to the
    // appropriate pattern node number
    for (Rule action : actionRuleMap.keySet()) {
      int act_id = actionRuleMap.get(action).intValue();

      if (action.getRight() != null) {
        // compute the set of replacement nodes preserved by this action
        Collection<Node> replacement_nodes_preserved = new HashSet<Node>();
        replacement_nodes_preserved.addAll(action.getRight().getNodes());
        replacement_nodes_preserved.retainAll(action.getPattern().getNodes());

        // for all those preserved replacement nodes store the
        // corresponding pattern node
        for (Node node : replacement_nodes_preserved) {
          int node_num = replacement_node_num.get(act_id).get(node).intValue();
          replacementNodeIsPreservedNode[act_id][node_num] =
              pattern_node_num.get(act_id).get(node).intValue();
        }
      }
    }
  }
  /** Method collectReplacementNodeChangesTypeToInfo */
  private void collectReplacementNodeChangesTypeToInfo() {
    replacementNodeChangesTypeTo = new int[n_graph_actions][max_n_replacement_nodes];

    // init the array with -1
    for (int i = 0; i < n_graph_actions; i++)
      for (int j = 0; j < max_n_replacement_nodes; j++) replacementNodeChangesTypeTo[i][j] = -1;

    // for all nodes preserved set the corresponding array entry to the
    // appropriate node type id
    for (Rule action : actionRuleMap.keySet()) {
      int act_id = actionRuleMap.get(action).intValue();

      if (action.getRight() != null) {
        for (Node node : action.getRight().getNodes()) {
          if (!node.changesType(action.getRight())) continue;

          int node_num = replacement_node_num.get(act_id).get(node).intValue();

          NodeType old_type = node.getNodeType();
          NodeType new_type = node.getRetypedNode(action.getRight()).getNodeType();

          if (!nodeTypeMap.get(old_type).equals(nodeTypeMap.get(new_type)))
            replacementNodeChangesTypeTo[act_id][node_num] = nodeTypeMap.get(new_type).intValue();
        }
      }
    }
  }
  /** Method collectNewInsertEdgesInfo */
  private void collectNewInsertEdgesInfo() {
    // Collection[] new_edges_of_action;
    newEdgesOfAction = new Vector<Collection<Edge>>(n_graph_actions);

    // init the array with empty HashSets
    for (int i = 0; i < n_graph_actions; i++) newEdgesOfAction.set(i, new HashSet<Edge>());

    // for all actions collect the edges to be newly inserted
    for (Rule action : actionRuleMap.keySet()) {
      int act_id = actionRuleMap.get(action).intValue();

      if (action.getRight() != null) {
        Graph replacement = action.getRight();
        // compute the set of newly inserted edges
        newEdgesOfAction.get(act_id).addAll(replacement.getEdges());
        newEdgesOfAction.get(act_id).removeAll(action.getPattern().getEdges());
      }
    }
  }
  /**
   * La methode principale qui renvoie l'arbre remplace si il y a matching et null sinon
   *
   * @param rule la rule qui va nous permettre de tester notre arbre.
   * @param arbre l'arbre a tester
   * @return le return qui vaut null si ça ne match pas, et qui vaut l'arbre remplacé sinon
   */
  public Expression check(Rule rule, Expression arbre) {
    boolean check;
    // ici on recupere le pattern de la rule et l'arbre
    Expression pattern = rule.getLeft();

    // Maintenant on homogeneise les noms de variable
    Hashtable<Character, Expression> ht = new Hashtable<Character, Expression>();

    check = this.compareExpressions(pattern, arbre, ht);
    if (check == false) return null;

    return replace(rule.getRight(), ht);
  }
  public boolean apply(Rule r) {
    //		System.out.println("ApplRuleSequencesGraTraImpl.apply(Rule) : "+r.getName()+"
    // "+updateTypeObjectsMapAfterStep);

    //		long time0 = System.currentTimeMillis();

    this.stoppingRule = false;
    boolean result = false;
    boolean valid = false;

    this.currentMatch = r.getMatch();
    if (this.currentMatch == null) {
      this.currentMatch = this.grammar.createMatch(r);
      this.currentMatch.setCompletionStrategy(
          (MorphCompletionStrategy) this.strategy.clone(), true);
      //		strategy.showProperties();
    } else if (this.updateTypeObjectsMapAfterStep) {
      this.currentMatch.setTypeObjectsMapChanged(true);
    }

    boolean parallelApply = true;
    boolean is_applied = false;
    //		int matchCompletions = 0;

    //		time0 = System.currentTimeMillis();

    while (parallelApply) {

      if (!isInputParameterSet(r.getLeft(), true, this.currentMatch)) {
        fireGraTra(new GraTraEvent(this, GraTraEvent.INPUT_PARAMETER_NOT_SET, this.currentMatch));
      }

      if (this.stopping || this.stoppingRule) {
        this.currentMatch.clear();
        return false;
      }

      if (this.pauseRule) return false;

      valid = false;
      while (!valid) {
        if (this.currentMatch.isTotal() || this.currentMatch.nextCompletion()) {
          if (this.currentMatch.isValid()) {
            valid = true;
            //						matchCompletions++;

            if (r.isParallelApplyEnabled() && this.currentMatch.typeObjectsMapChanged) {
              this.currentMatch.typeObjectsMapChanged = false;
              // das hat Auswirkung auf den naechsten Aufruf
              // von nextCompletion():
              // die Graphaenderungen nach dem Step werden
              // NICHT BEACHTET!!!
            }
          } else {
            this.errorMsg = this.currentMatch.getErrorMsg();
            this.currentMatch.clear();
          }
        } else {
          this.errorMsg = this.currentMatch.getErrorMsg();
          break;
        }
      }

      if (valid) {

        fireGraTra(new GraTraEvent(this, GraTraEvent.MATCH_VALID, this.currentMatch));

        if (!isInputParameterSet(r.getRight(), false, this.currentMatch)) {
          fireGraTra(new GraTraEvent(this, GraTraEvent.INPUT_PARAMETER_NOT_SET, this.currentMatch));
        }

        if (this.stopping || this.stoppingRule) {
          if (this.currentMatch != null) {
            this.currentMatch.clear();
          }
          return false;
        }

        if (this.pauseRule) return false;

        try { // check attr context: variables only
          boolean checkVarsOnly = true;
          this.currentMatch
              .getAttrContext()
              .getVariables()
              .getAttrManager()
              .checkIfReadyToTransform(this.currentMatch.getAttrContext(), checkVarsOnly);
        } catch (AttrException ex) {
          fireGraTra(new GraTraEvent(this, GraTraEvent.NOT_READY_TO_TRANSFORM, r.getName()));
          // destroyMatch(currentMatch);
          return false;
        }

        Morphism coMatch = apply(this.currentMatch);
        if (coMatch != null) {
          this.errorMsg = "";
          is_applied = true;

          this.currentMatch.clear();
          // destroyMatch(currentMatch);
          coMatch.dispose();
          coMatch = null;
          result = true;
        } else {
          valid = false;
          fireGraTra(
              new GraTraEvent(this, GraTraEvent.NO_COMPLETION, this.currentMatch, this.errorMsg));
          this.currentMatch.clear();
          // destroyMatch(currentMatch);

          result = false;
        }
      } else {
        fireGraTra(
            new GraTraEvent(
                this,
                GraTraEvent.NO_COMPLETION,
                this.currentMatch,
                this.currentMatch.getErrorMsg()));
        this.currentMatch.clear();
        // destroyMatch(currentMatch);

        result = false;
      }

      //
      if (r.isParallelApplyEnabled()) {
        if (!valid) {
          parallelApply = false;
          this.currentMatch.typeObjectsMapChanged = true;
        }
        if (is_applied) {
          result = true;
        }
      } else {
        parallelApply = false;
        break;
      }
      //
    }

    return result;
  }
  /*
   * collect some information needed for code gen process of the data
   * structures representing the graph actions
   *
   */
  protected void collectActionInfo() {
    /* get the overall number of graph actions */
    n_graph_actions = actionRuleMap.keySet().size();

    /* get the overall maximum numbers of nodes and edges of all pattern
    and replacement graphs respectively */
    max_n_pattern_nodes = 0;
    max_n_pattern_edges = 0;
    max_n_replacement_nodes = 0;
    max_n_replacement_edges = 0;

    for (Rule act : actionRuleMap.keySet()) {
      // check whether its graphs node and edge set sizes are greater
      int size;

      size = act.getPattern().getNodes().size();
      if (size > max_n_pattern_nodes) max_n_pattern_nodes = size;
      size = act.getPattern().getEdges().size();
      if (size > max_n_pattern_edges) max_n_pattern_edges = size;

      if (act.getRight() != null) {
        size = act.getRight().getNodes().size();
        if (size > max_n_replacement_nodes) max_n_replacement_nodes = size;
        size = act.getRight().getEdges().size();
        if (size > max_n_replacement_edges) max_n_replacement_edges = size;
      }
    }

    /* compute the numbers of nodes/edges of all pattern/replacement-graphs */
    pattern_node_num = new Vector<Map<Node, Integer>>(n_graph_actions);
    pattern_edge_num = new Vector<Map<Edge, Integer>>(n_graph_actions);
    replacement_node_num = new Vector<Map<Node, Integer>>(n_graph_actions);
    replacement_edge_num = new Vector<Map<Edge, Integer>>(n_graph_actions);
    for (Rule act : actionRuleMap.keySet()) {
      int act_id = actionRuleMap.get(act).intValue();
      assert act_id < n_graph_actions
          : "action id found which was greater than the number of graph actions";

      // compute node/edge numbers
      pattern_node_num.set(act_id, new HashMap<Node, Integer>());
      pattern_edge_num.set(act_id, new HashMap<Edge, Integer>());

      // fill the map with pairs (node, node_num)
      int node_num = 0;

      for (Node node : act.getPattern().getNodes()) {
        pattern_node_num.get(act_id).put(node, new Integer(node_num++));
      }
      assert node_num == act.getPattern().getNodes().size()
          : "Wrong number of node_nums was created";

      // fill the map with pairs (edge, edge_num)
      int edge_num = 0;

      for (Edge edge : act.getPattern().getEdges()) {
        pattern_edge_num.get(act_id).put(edge, new Integer(edge_num++));
      }
      assert edge_num == act.getPattern().getEdges().size()
          : "Wrong number of edge_nums was created";

      // if action has a replacement graph, compute node/edge numbers
      if (act.getRight() != null) {
        replacement_node_num.set(act_id, new HashMap<Node, Integer>());
        replacement_edge_num.set(act_id, new HashMap<Edge, Integer>());

        // fill the map with pairs (node, node_num)
        node_num = 0;

        for (Node node : act.getRight().getNodes()) {
          replacement_node_num.get(act_id).put(node, new Integer(node_num++));
        }
        assert node_num == act.getRight().getNodes().size()
            : "Wrong number of node_nums was created";

        // fill the map with pairs (edge, edge_num)
        edge_num = 0;

        for (Edge edge : act.getRight().getEdges()) {
          replacement_edge_num.get(act_id).put(edge, new Integer(edge_num++));
        }
        assert edge_num == act.getRight().getEdges().size()
            : "Wrong number of edge_nums was created";
      } else {
        replacement_node_num.set(act_id, null);
        replacement_edge_num.set(act_id, null);
      }
    }

    /* for all actions decompose the conditions into conjunctive parts,
    give all these subexpessions a number, and setup some maps keeping
    information about them */
    // init a subexpression counter
    int subConditionCounter = 0;

    // setup the array for conditions
    conditions = new HashMap<Integer, Collection<Expression>>();

    // iterate over all actions
    for (Rule act : actionRuleMap.keySet()) {
      int act_id = actionRuleMap.get(act).intValue();

      conditions.put(act_id, new TreeSet<Expression>(conditionsComparator));

      // iterate over all conditions of the current action
      for (Expression condition : act.getPattern().getConditions()) {

        // divide the expression to all AND-connected parts, which do
        // not have an AND-Operator as root themselves
        Collection<Expression> subConditions = decomposeAndParts(condition);

        // for all the subconditions just computed...
        for (Expression sub_condition : subConditions) {

          // ...create condition numbers
          conditionNumbers.put(sub_condition, new Integer(subConditionCounter++));

          // ...extract the pattern nodes and edges involved in the condition
          Collection<Node> involvedNodes = collectInvolvedNodes(sub_condition);
          Collection<Edge> involvedEdges = collectInvolvedEdges(sub_condition);
          // and at these Collections to prepared Maps
          conditionsInvolvedNodes.put(sub_condition, involvedNodes);
          conditionsInvolvedEdges.put(sub_condition, involvedEdges);

          // store the subcondition in an ordered Collection
          conditions.get(act_id).add(sub_condition);
        }
      }
    }
    // store the overall number of (sub)conditions
    n_conditions = subConditionCounter;

    /* collect the type constraints of the node of all actions pattern graphs */
    int typeConditionCounter = n_conditions;
    typeConditions = new Vector<Collection<Collection<InheritanceType>>>(n_graph_actions);

    for (Rule act : actionRuleMap.keySet()) {
      int act_id = actionRuleMap.get(act).intValue();

      typeConditions.set(
          act_id, new TreeSet<Collection<InheritanceType>>(typeConditionsComparator));

      /* for all nodes of the current MatchingActions pattern graph
      extract that nodes type constraints */
      PatternGraph pattern = act.getPattern();
      for (Node node : pattern.getNodes()) {
        // if node has type constraints, register the as conditions
        if (!node.getConstraints().isEmpty()) {

          // note that a type condition is the set of all types,
          // the corresponding node/edge is not allowed to be of
          Collection<InheritanceType> type_condition = node.getConstraints();

          // ...create condition numbers
          typeConditionNumbers.put(type_condition, new Integer(typeConditionCounter++));

          // ...extract the pattern nodes and edges involved in the condition
          Collection<Node> involvedNodes = new HashSet<Node>();
          involvedNodes.add(node);
          // and at these Collections to prepared Maps
          typeConditionsInvolvedNodes.put(type_condition, involvedNodes);
          Collection<Edge> empty = Collections.emptySet();
          typeConditionsInvolvedEdges.put(type_condition, empty);

          // store the subcondition in an ordered Collection
          typeConditions.get(act_id).add(type_condition);
        }
      }
      // do the same thing for all edges of the current pattern
      for (Edge edge : pattern.getEdges()) {

        // if node has type constraints, register the as conditions
        if (!edge.getConstraints().isEmpty()) {

          // note that a type condition is the set of all types,
          // the corresponding edge is not allowed to be of
          Collection<InheritanceType> type_condition = edge.getConstraints();

          // ...create condition numbers
          typeConditionNumbers.put(type_condition, new Integer(typeConditionCounter++));

          // ...extract the pattern edges and edges involved in the condition
          Collection<Edge> involvedEdges = new HashSet<Edge>();
          involvedEdges.add(edge);
          // and at these Collections to prepared Maps
          Collection<Node> empty = Collections.emptySet();
          typeConditionsInvolvedNodes.put(type_condition, empty);
          typeConditionsInvolvedEdges.put(type_condition, involvedEdges);

          // store the subcondition in an ordered Collection
          typeConditions.get(act_id).add(type_condition);
        }
      }
    }
    // update the overall number of conditions, such that type
    // conditions are also included
    n_conditions = typeConditionCounter;

    /* for all conditions (not type conditions!) the pairs
    (pattern_node_num, attr_id), which occur
    in qualifications at the leaves of the condition, are needed.
    To obtain that compute a map
    condition_num -> pattern_node_num_ -> { attr_ids }
    implemented by an Array of Maps; usage is:
    involvedPatternNodeAttrIds[cond_num].get(pattern_node_num)
    which yields a Collection of attr-ids.
    */
    involvedPatternNodeAttrIds = new HashMap<Expression, Map<Node, Collection<Integer>>>();
    involvedPatternEdgeAttrIds = new HashMap<Expression, Map<Edge, Collection<Integer>>>();

    for (Rule act : actionRuleMap.keySet()) {
      int act_id = actionRuleMap.get(act).intValue();

      // collect the attr ids in dependency of condition and the pattern node
      for (Expression cond : conditions.get(act_id)) {
        // TODO use or remove it
        // int cond_num = conditionNumbers.get(cond).intValue();

        // descent to the conditions leaves and look for qualifications
        Map<Node, Collection<Integer>> node_map = new HashMap<Node, Collection<Integer>>();
        Map<Edge, Collection<Integer>> edge_map = new HashMap<Edge, Collection<Integer>>();
        __recursive_qual_collect(act_id, node_map, edge_map, cond);
        involvedPatternNodeAttrIds.put(cond, node_map);
        involvedPatternEdgeAttrIds.put(cond, edge_map);
      }
    }

    /* for each action compute the start node used in the matching process */
    // init the array of start nodes
    start_node = new Node[n_graph_actions];
    // for all actions gen matcher programs
    for (Rule action : actionRuleMap.keySet()) {
      Graph pattern = action.getPattern();

      // pick out the node with the highest priority as start node
      int max_prio = 0;
      // get any node as initial node
      Node max_prio_node = null;
      if (pattern.getNodes().iterator().hasNext()) {
        max_prio_node = pattern.getNodes().iterator().next();
      }
      for (Node node : pattern.getNodes()) {
        // get the nodes priority
        int prio = 0;
        Annotations a = node.getAnnotations();
        if (a != null)
          if (a.containsKey("prio") && a.isInteger("prio"))
            prio = ((Integer) a.get("prio")).intValue();

        // if the current priority is greater, update the maximum priority node
        if (prio > max_prio) {
          max_prio = prio;
          max_prio_node = node;
        }
      }
      start_node[actionRuleMap.get(action).intValue()] = max_prio_node;
    }
    // collect information about potential homomorphic pattern graph nodes,
    // i.e. nodes that are allowed to be identified by the matcher during the
    // matching process
    collectPotHomInfo();
    collectPatternNodesToBeKeptInfo();
    collectReplacementNodeIsPreservedNodeInfo();
    collectReplacementNodeChangesTypeToInfo();
    collectNewInsertEdgesInfo();
  }