// utils.beh -- utility functions and traversals
// $Id: utils.beh,v 1.10 2003/01/30 02:26:05 dougo Exp $

SGEdge {
  boolean isSource() {{ return sourcemarker != null; }}
  boolean isTarget() {{ return targetmarker != null; }}
}

SymbolicNameMapI {
  ClassGlobSpec get(String l);
}

NameMap {
  /** A glob of class graph names corresponding to constraint label l,
      or null if l is not in the map. */
  public ClassGlobSpec get(String l) {{
    return (ClassGlobSpec) get_dict().get(l);
  }}
  {{ HashMap dict; }}
  HashMap get_dict() {{
    if (dict == null) dict = buildDict();
    return dict;
  }}
  HashMap buildDict()
    // FIXME: to-stop an alternation class doesn't work!
    to-stop { Name, /* ClassGlobSpec */ OneClassGlob, ClassGlobSet }
  {
    init {{ return_val = new HashMap(); }}
    {{ Name sgName; ClassGlobSpec cgNames; }}
    before Name {{ sgName = host; }}
    before ClassGlobSpec {{ cgNames = host; }}
    after NameBinding {{
      return_val.put(sgName.toString(), cgNames);
    }}
  }
  void addElement(NameBinding obj) {{
    get_dict().put(obj.get_sgName().toString(), obj.get_cgNames());
    if (bindings == null) bindings = new NameBinding_Commalist();
    bindings.addElement(obj);
  }}
  static NameMapI compose(NameMapI map1, SymbolicNameMapI map2) {{
    final NameMapI N1 = map1;
    final SymbolicNameMapI N2 = map2;
    return new NameMapI() {
	public Collection get(String l, ClassGraphI CG)
	  throws NoSuchClassGraphNodeException
	{
	  ClassGlobSpec N2ret = N2.get(l);
	  if (N2ret == null) return N1.get(l, CG);
	  Collection strings = N2ret.getStrings();
	  if (strings == null) return null;
	  Collection ret = new HashSet();
	  Iterator i = strings.iterator();
	  while (i.hasNext()) {
	    Collection N1ret = N1.get((String) i.next(), CG);
	    if (N1ret == null) return null;
	    ret.addAll(N1ret);
	  }
	  return ret;
	}
	public boolean match(String l, Object cgv)
	{ return N1.match(l, cgv) || N2.match(l, cgv); }
	public boolean match(String l, String cgl)
	{ return N1.match(l, cgl) || N2.match(l, cgl); }
	public String toString()
	{ return N1 + " o " + N2; }
      };	    
  }}
  static SymbolicNameMapI compose(SymbolicNameMapI map1, SymbolicNameMapI map2)
  {{
    final SymbolicNameMapI N1 = map1;
    final SymbolicNameMapI N2 = map2;
    return new SymbolicNameMap() {
	public ClassGlobSpec get(String l) {
	  ClassGlobSpec N2ret = N2.get(l);
	  if (N2ret == null) return N1.get(l);
	  Collection strings = N2ret.map(N1);
	  if (strings == null)
	    return OneClassGlob.parse("*");
	  Iterator i = strings.iterator();
	  String s = (String) i.next();
	  if (!i.hasNext()) return OneClassGlob.parse(s);
	  while (i.hasNext()) s += ", " + i.next();
	  return ClassGlobSet.parse("{" + s + "}");
	}
	public String toString()
	{ return N1 + " o " + N2; }
      };	    
  }}

  public String toString() {{
    return "{ " + (bindings == null? "" : bindings.toString()) + " }";
  }}
}

{ ClassGlobSet, GlobSet } {
  public String toString() {{
    return "{ " + (globs == null ? "" : globs.toString()) + " }";
  }}
}
{ Glob_Commalist, ClassGlob_Commalist, NameBinding_Commalist } {
  public String toString() {{
    return (first == null ? "" : first.toString());
  }}
}
{ Nonempty_Glob_Commalist, Nonempty_ClassGlob_Commalist,
  Nonempty_NameBinding_Commalist }
{
  public String toString() {{
    return it + (next == null ? "" : ", " + next);
  }}
}
NameBinding
{ public String toString() {{ return sgName + "=" + cgNames; }} }
OneGlob
{ public String toString() {{ return glob.toString(); }} }
OneClassGlob
{ public String toString() {{ return classglob.toString(); }} }
{ ClassName, PartName, ClassGlob }
{ public String toString() {{ return name.toString(); }} }
ClassNameExact
{ public String toString() {{ return classname.toString(); }} }
AnyClass
{ public String toString() {{ return "*"; }} }
Name
{ public String toString() {{ return first.toString(); }} }
Nonempty_Name {
  public String toString() {{
    return it + (next == null ? "" : "." + next);
  }}
}

StrategyCombination {
  public String toString() {{
    return "(" + first + ", " + rest + ")";
  }}
}
Strategy_Commalist {
  public String toString() {{ return first.toString(); }}
}
Nonempty_Strategy_Commalist {
  public String toString() {{
    return it + (next == null ? "" : ", " + next);
  }}
}
Intersect {
  public String toString() {{ return "intersect" + super.toString(); }}
}
Join {
  public String toString() {{ return "join" + super.toString(); }}
}
Merge {
  public String toString() {{ return "merge" + super.toString(); }}
}

{ StrategyGraph, PathDirective, SGEdge, StrategyReference } {
  public String toString() {{
    StringWriter w = new StringWriter();
    PrintWriter pw = new PrintWriter(w);
    universal_trv0(new PrintVisitor(pw));
    pw.flush();
    return w.toString();
  }}
}

{ ClassName, PartName, ClassGlob, ClassGlobSpec, GlobSpec } {
  // FIXME: could be sped up by writing out the recursive equality test.
  public boolean equals(Object obj) {{
    return toString().equals(obj.toString());
  }}
  // FIXME: it would be nice if this were O(1)...
  public int hashCode() {{
    return toString().hashCode();
  }}
}

GlobSpec {
  GlobSpec deepCopy() {{
    CopyVisitor c = new CopyVisitor(getClass());
    universal_trv0(c);
    return (GlobSpec) c.get_copy();
  }}
}    

ClassGlobSpec {
  ClassGlobSpec deepCopy() {{
    CopyVisitor c = new CopyVisitor(getClass());
    universal_trv0(c);
    return (ClassGlobSpec) c.get_copy();
  }}
}    

{ Strategy, SGEdge } {
  public Object clone() {{
    try {
      return super.clone();
    } catch (CloneNotSupportedException e) {
      throw new RuntimeException(e);
    }
  }}
}