// interface.beh -- interface method implementations.
// $Id: interface.beh,v 1.14 2003/01/30 22:32:25 dougo Exp $

Strategy {
  public boolean isSimpleStrategy() {{
    return false;
  }}
  public SimpleStrategyI toSimpleStrategy() {{
    return (SimpleStrategyI) this;
  }}
}
SimpleStrategy {
  public boolean isSimpleStrategy() {{ return true; }}
}
StrategyGraph {
  public SimpleStrategyI toSimpleStrategy() {{ return toGraph(); }}
}
PathDirective {
  public SimpleStrategyI toSimpleStrategy() {{ return getGraph(); }}
}


Strategy {
  public boolean isStrategyCombination() {{
    return false;
  }}
  public StrategyCombinationI toStrategyCombination() {{
    return (StrategyCombinationI) this;
  }}
}
StrategyCombination {
  public boolean isStrategyCombination() {{ return true; }}
}


StrategyCombination {
  public boolean isStrategyIntersection() {{ return false; }}
}
Intersect {
  public boolean isStrategyIntersection() {{ return true; }}
}

StrategyCombination {
  {{ Strategy left, right; }}
  public StrategyI getLeftStrategy() {{
    if (left == null) left = first;
    return left;
  }}
  public StrategyI getRightStrategy() {{
    if (right == null) {
      Strategy next = rest.first.it;
      if (rest.first.next == null) {
	right = next;
      } else {
	StrategyCombination r = (StrategyCombination) clone();
	r.first = next;
	r.rest = new Strategy_Commalist(rest.first.next);
	r.left = r.right = null;
	right = r;
      }
    }
    return right;
  }}
}

StrategyGraph {
  // SimpleStrategyI:

  /** The strategy graph S. */
  public StrategyGraphI getStrategyGraph() {{ return this; }}
  
  /** An unmodifiable Collection of source nodes. */
  public Collection getSources() {{
    return Collections.unmodifiableCollection(sources);
  }}

  /** An unmodifiable Collection of target nodes. */
  public Collection getTargets() {{
    return Collections.unmodifiableCollection(targets);
  }}

  /** The set N(v), a collection of symbolic names (strings)
      corresponding to node v in the strategy graph S. */
  public Collection getNames(Object v) {{
    return ((GlobSpec) v).collectClassGlobs().map(nameMap);
  }}

  /** The constraint map B.  Return null for B_true, in which all
      predicates return true. */
  public ConstraintMapI getConstraintMap() {{ return this; }}

  // StrategyGraphI:

  /** An unmodifiable Collection of nodes in the strategy graph. */
  public Collection getNodes() {{
    return Collections.unmodifiableCollection(nodes.keySet());
  }}

  /** An unmodifiable Collection of indices (Integers) of the edges coming
      into node v.  Return an empty list if there are no incoming
      edges, null if there is no such node. */
  public Collection getIncomingEdges(Object v) {{
    IncidentEdges ie = (IncidentEdges) nodes.get(v);
    return ie == null ? null : ie.incoming;
  }}

  /** An unmodifiable Collection of indices (Integers) of the edges going
      out of node v.  Return an empty list if there are no outgoing
      edges, null if there is no such node. */
  public Collection getOutgoingEdges(Object v) {{
    IncidentEdges ie = (IncidentEdges) nodes.get(v);
    return ie == null ? null : ie.outgoing;
  }}

  /** The number of edges in the strategy graph. */
  public int numEdges() {{ return edgeList.size(); }}

  /** The source node of edge number i. */
  public Object getEdgeSource(int i) {{
    return ((SGEdge) edgeList.get(i)).get_source();
  }}

  /** The target node of edge number i. */
  public Object getEdgeTarget(int i) {{
    return ((SGEdge) edgeList.get(i)).get_target();
  }}


  // ConstraintMapI:

  /** Is the element predicate for the ith edge in the strategy graph
      true for the class graph node v, using name map N? */
  public boolean meetsConstraint(int i, Object v, NameMapI N) {{
    SGEdge sgedge = (SGEdge) edgeList.get(i);
    return sgedge.meetsConstraint(v, composeNameMap(N));
  }}

  /** Is the element predicate for the ith edge in the strategy graph
      true for the class graph edge e, using name map N? */
  public boolean meetsConstraint(int i, EdgeI e, NameMapI N) {{
    SGEdge sgedge = (SGEdge) edgeList.get(i);
    return sgedge.meetsConstraint(e, composeNameMap(N));
  }}

  /** Is the element predicate for the node a in the strategy graph
      true for the class graph node v, using name map N? */
  public boolean meetsConstraint(Object a, Object v, NameMapI N) {{
    return ((GlobSpec) a).match(v, composeNameMap(N));
  }}

  /** Is the element predicate for the node a in the strategy graph
      true for the class graph edge e, using name map N? */
  public boolean meetsConstraint(Object a, EdgeI e, NameMapI N) {{
    return ((GlobSpec) a).match(e, composeNameMap(N));
  }}

  /** Construct the composition of N and the encapsulated name map.
      Memoize the result. */
  NameMapI composeNameMap(NameMapI N) {{
    if (N != memoN) {
      memoN = N;
      if (N == null)
	composedN = nameMap;
      else if (nameMap == null)
	composedN = N;
      else
	composedN = NameMap.compose(N, nameMap);
    }
    return composedN;
  }}
  {{ NameMapI memoN, composedN; }}
}

SymbolicNameMap {
  /** A collection of nodes in class graph G corresponding to symbolic
      node name l in the strategy, or null if l is a wildcard (all
      nodes in the class graph). */
  public Collection get(String l, ClassGraphI G)
    throws NoSuchClassGraphNodeException
  {{
    ClassGlobSpec glob = get(l);
    if (glob == null)
      return Collections.singleton(G.getNode(l));
    Collection strings = glob.getStrings();
    if (strings == null) return null;
    Collection nodes = new HashSet();
    Iterator i = strings.iterator();
    while (i.hasNext()) {
      nodes.add(G.getNode((String) i.next()));
    }
    return nodes;
  }}

  /** Does symbolic node name l map to class graph node cgv? */
  public boolean match(String l, Object cgv) {{
    ClassGlobSpec cgNames = get(l);
    return cgNames == null
      ? String.valueOf(cgv).equals(l)
      : cgNames.match(cgv, null);
  }}

  /** Does symbolic edge label l map to class graph edge label cgl? */
  public boolean match(String l, String cgl) {{
    ClassGlobSpec cgNames = get(l);
    return cgNames == null
      ? cgl.equals(l)
      : cgNames.match(cgl, null);
  }}
}

Strategy {
  /**
   * An unmodifiable set of symbolic names that the source nodes in
   * the strategy graph map to, or null if the strategy can start at
   * any class graph node.
   */
  public abstract Set getSourceNames();
  /**
   * An unmodifiable set of symbolic names that the target nodes in
   * the strategy graph map to, or null if the strategy can finish at
   * any class graph node.
   */
  public abstract Set getTargetNames();
}

SimpleStrategy {
  public Set getSourceNames() {{
    return getAllNames(toSimpleStrategy().getSources());
  }}
  public Set getTargetNames() {{
    return getAllNames(toSimpleStrategy().getTargets());
  }}
  /**
   * An unmodifiable set of symbolic names that the given nodes in the
   * strategy graph map to, or null if one or more of the nodes is a
   * wildcard node that can map to all class graph nodes.
   */
  public Set getAllNames(Collection nodes) {{
    Set names = new LinkedHashSet();
    for (Iterator it = nodes.iterator(); it.hasNext();) {
      Collection nodeNames = toSimpleStrategy().getNames(it.next());
      if (nodeNames == null)
	return null;
      else
	names.addAll(nodeNames);
    }
    return Collections.unmodifiableSet(names);
  }}
}

StrategyCombination {
  public Set getSourceNames() {{
    return combineNames(((Strategy) getLeftStrategy()).getSourceNames(),
			((Strategy) getRightStrategy()).getSourceNames());
  }}
  public Set getTargetNames() {{
    return combineNames(((Strategy) getLeftStrategy()).getTargetNames(),
			((Strategy) getRightStrategy()).getTargetNames());
  }}
  public abstract Set combineNames(Set l, Set r);
}

Intersect {
  public Set combineNames(Set l, Set r) {{
    if (l == null) return r;
    if (r == null) return l;
    Set combined = new LinkedHashSet(l);
    combined.retainAll(r);
    return Collections.unmodifiableSet(combined);
  }}
}

{ Join, Merge } {
  public Set combineNames(Set l, Set r) {{  
    throw new RuntimeException("Join and Merge not yet implemented.");
  }}
}

StrategyReference {
  {{ Map env; Strategy strategy; }}
  Strategy deref() {{
    if (strategy == null) {
      strategy = (Strategy) env.get(ident.toString());
      if (strategy == null)
	throw new RuntimeException("No such strategy: " + ident);
    }
    return strategy;
  }}
  public boolean isSimpleStrategy() {{
    return deref().isSimpleStrategy();
  }}
  public SimpleStrategyI toSimpleStrategy() {{
    return deref().toSimpleStrategy();
  }}
  public boolean isStrategyCombination() {{
    return deref().isStrategyCombination();
  }}
  public StrategyCombinationI toStrategyCombination() {{
    return deref().toStrategyCombination();
  }}
  public Set getSourceNames() {{
    return deref().getSourceNames();
  }}
  public Set getTargetNames() {{
    return deref().getTargetNames();
  }}
}