// sg.beh -- behavior for creating strategy graphs
// $Id: sg.beh,v 1.16 2003/01/30 02:26:04 dougo Exp $

Strategy {
  /** Read a strategy expression from a byte stream. */
  public static Strategy readFrom(InputStream in) throws ParseException {{
    return readFrom(in, new HashMap());
  }}
  /** Read a strategy expression from a char stream. */
  public static Strategy readFrom(Reader in) throws ParseException {{
    return readFrom(in, new HashMap());
  }}
  /** Convert a string to a strategy. */
  public static Strategy fromString(String s) {{
    return fromString(s, new HashMap());
  }}

  /**
   * Read a strategy expression from a byte stream.
   * Strategy references are resolved by looking them up in the
   * provided environent.
   */
  public static Strategy readFrom(InputStream in, Map env)
    throws ParseException
  {{
    Strategy s =  StrategyExpression.parse(in).get_strategy();
    s.attachEnv(env);
    return s;
  }}
  /**
   * Read a strategy expression from a char stream.
   * Strategy references are resolved by looking them up in the
   * provided environent.
   */
  public static Strategy readFrom(Reader in, Map env)
    throws ParseException
  {{
    Strategy s = StrategyExpression.parse(in).get_strategy();
    s.attachEnv(env);
    return s;
  }}
  /**
   * Convert a string to a strategy.
   * Strategy references are resolved by looking them up in the
   * provided environent.
   */
  public static Strategy fromString(String in, Map env) {{
    Strategy s = StrategyExpression.parse(in).get_strategy();
    s.attachEnv(env);
    return s;
  }}
}

Strategy {
  void attachEnv(Map env) to StrategyReference {
    before StrategyReference {{ host.env = env; }}
  }
}

StrategyGraph {
  {{
    List edgeList = new ArrayList();
    class IncidentEdges {
      List incoming = new ArrayList(), outgoing = new ArrayList();
    }
    Map nodes = new HashMap(); // node -> IncidentEdges
    Set sources = new HashSet(), targets = new HashSet();
    SymbolicNameMapI nameMap = null;
  }}
  init {{ edges = new SGEdge_SList(); }}
  StrategyGraph toGraph() {{
    if (edgeList.isEmpty()) {
      Enumeration e = edges.elements();
      int i = 0;
      Set allSources = new HashSet(), allTargets = new HashSet();
      while (e.hasMoreElements()) {
	SGEdge edge = (SGEdge) e.nextElement();
	addEdge(edge, i++);
	allSources.add(edge.get_source());
	allTargets.add(edge.get_target());
      }
      if (sources.isEmpty()) sources = allSources;
      if (targets.isEmpty()) targets = allTargets;
      if (nameMap == null)
	nameMap = (parsedNameMap == null ? new NameMap() : parsedNameMap);
      parsedNameMap = null;
    }
    return this;
  }}

  /* Add an edge to the strategy graph. */
  public void addEdge(SGEdge edge) {{
    addEdge(edge, edges.size());
    edges.addElement(edge);
  }}
  void addEdge(SGEdge edge, int i) {{
    edgeList.add(edge);

    GlobSpec source = edge.get_source();
    if (edge.isSource()) sources.add(source);
    IncidentEdges ie = (IncidentEdges) nodes.get(source);
    if (ie == null) nodes.put(source, ie = new IncidentEdges());
    ie.outgoing.add(new Integer(i));

    GlobSpec target = edge.get_target();
    if (edge.isTarget()) targets.add(target);
    ie = (IncidentEdges) nodes.get(target);
    if (ie == null) nodes.put(target, ie = new IncidentEdges());
    ie.incoming.add(new Integer(i));
  }}
}

PathDirective {
  {{ StrategyGraph graph; }}
  public StrategyGraph getGraph() {{
    if (graph == null) graph = toGraph();
    return graph;
  }}
}

PathDirective {
  StrategyGraph toGraph()
    to { From, NegativeConstraint, PositiveConstraint, To, ToStop, NameMap }
  {
    {{ SGEdge edge; }}
    {{ NameMap map = new NameMap(); }}
    before PathDirective {{
      return_val = new StrategyGraph();
      return_val.nameMap = map;
    }}
    before From {{
      ClassGlobSpec sourcespec = host.get_sources();
      edge = new SGEdge();
      edge.set_source(sourcespec.toGlobSpec());
      edge.set_sourcemarker(new SourceMarker());
    }}
    before NegativeConstraint {{
      edge.set_constraint(host);
    }}
    before PositiveConstraint {{
      GlobSpec glob = host.get_glob();
      edge.set_target(glob);
      return_val.addEdge(edge);
      edge = new SGEdge();
      edge.set_source(glob);
    }}
    before ToStop {{
      // Bypass the target(s).
      Bypassing constraint =
	Bypassing.parse("bypassing " + host.get_targets());
      edge.set_constraint(constraint.intersectWith(edge.get_constraint()));
    }}
    after TargetDirective {{
      ClassGlobSpec targets = host.get_targets();
      edge.set_target(targets.toGlobSpec());
      edge.set_targetmarker(new TargetMarker());
      return_val.addEdge(edge);
    }}
    before NameMap {{
      return_val.nameMap = NameMap.compose(host, map);
    }}
  }
}

Bypassing {
  NegativeConstraint intersectWith(NegativeConstraint constraint) {{
    if (constraint == null) return this;
    if (constraint instanceof OnlyThrough) return constraint;
    return new Bypassing(glob.union(constraint.get_glob()));
  }}
}

Main {
  /** Testing stub. */
  public static void main(String args[]) {{
    BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
    try {
      Strategy s = Strategy.readFrom(in);
      System.out.println(s);
    } catch (ParseException e) {
      System.err.println(e.getMessage());
    }
  }}
}