// glob.beh -- Glob matching and manipulation
// $Id: glob.beh,v 1.8 2002/01/04 17:01:40 dougo Exp $

GlobSpec {
  /** Does class graph edge e match with an edge glob in this set,
      using name map N? */
  boolean match(EdgeI e, NameMapI N) {{ return matchEdge(e, N); }}
  // FIXME: Can't have multiple adaptive methods with the same name :(
  boolean matchEdge(EdgeI e, NameMapI N)
    bypassing ClassGlob
    to { ClassName, PartName }
  {
    {{ boolean edgeglob_matches; }}
    before EdgeGlob {{ edgeglob_matches = false; }}
    after EdgeGlob {{ if (edgeglob_matches) return_val = true; }}
    
    around PartGlob {{
      if (e.isConstructionEdge()) {
	edgeglob_matches = true;
	subtraversal.apply();
      }
    }}
    around SubclassGlob {{
      if (e.isAlternationEdge()) {
	edgeglob_matches = true;
	subtraversal.apply();
      }
    }}
    around SuperclassGlob {{
      if (e.isInheritanceEdge()) {
	edgeglob_matches = true;
	subtraversal.apply();
      }
    }}

    {{ Object v; String l; }}
    before SourceGlob {{ v = e.getSource(); }}
    before PartNameGlob {{ l = e.getLabel(); }}
    before TargetGlob {{ v = e.getTarget(); }}
    before ClassName {{
      // There is no &&= in Java for some reason...
      if (edgeglob_matches)
	edgeglob_matches = host.match(v, N);
    }}
    before PartName {{
      if (edgeglob_matches)
	edgeglob_matches = host.match(l, N);
    }}
  }
}

{ GlobSpec, ClassGlobSpec } {
  /** Does the class graph node v match with a class glob in this set,
      using the name map N? */
  boolean match(Object v, NameMapI N)
    via ClassGlob
    to { ClassName, AnyClass }
  {
    before ClassName {{
      if (!return_val) return_val = host.match(v, N);
    }}
    before AnyClass {{
      return_val = true;
    }}
  }
}

ClassName {
  /** Does class graph node v match this classname, with name map N? */
  boolean match(Object v, NameMapI N) {{
    String s = toString();
    return N == null ? String.valueOf(v).equals(s) : N.match(s, v);
  }}
}
PartName {
  /** Does class graph edge label l match this partname, with name map N? */
  boolean match(String l, NameMapI N) {{
    String toMatch = toString();
    return N == null ? l.equals(toMatch) : N.match(toMatch, l);
  }}
}

ClassGlobSpec {
  /** A collection of strings corresponding to the classes in the
      glob, after applying the name map N.  Return null if wildcard. */
  Collection map(SymbolicNameMapI N) to { ClassName, AnyClass } {
    {{ Collection c = new ArrayList(); }}
    before AnyClass {{ c = null; }}
    before ClassName {{
      if (c != null) {
	String l = host.toString();
	ClassGlobSpec cgNames = N.get(l);
	if (cgNames == null)
	  c.add(l);
	else
	  c = cgNames.addStrings(c);
      }
    }}
    return {{ c }}
  }
  /** Add all class names in the glob as strings to c, returning c
      or null if wildcard. */
  Collection addStrings(Collection c) to { ClassName, AnyClass } {
    before AnyClass {{ c = null; }}
    before ClassName {{ if (c != null) c.add(host.toString()); }}
    return {{ c }}
  }
  /** A collection of class names in the glob as strings, or null
      if a wildcard. */
  Collection getStrings() {{ return addStrings(new ArrayList()); }}
}

ClassGlobSpec {
  traversal allGlobs(GlobVisitor v) { to ClassGlob; }
}

GlobSpec {
  traversal allGlobs(GlobVisitor v) {
    to { ClassGlob, PartGlob, SubclassGlob, SuperclassGlob };
  }
}

GlobVisitor {
  before { GlobSpec, ClassGlobSpec, Glob, ClassGlob,
	   PartGlob, SubclassGlob, SuperclassGlob } {{ }}
  before { OneGlob, GlobSet, OneClassGlob, ClassGlobSet, EdgeGlob } {{ }}
}

GlobSpec {
  /** Return a ClassGlobSpec that matches all the classes in the
      GlobSpec, or null if there are none. */
  ClassGlobSpec collectClassGlobs() to ClassGlob {
    {{ OneClassGlob one; ClassGlobSet set; }}
    before ClassGlob {{
      if (one == null) {
	one = new OneClassGlob(host);
      } else {
	if (set == null) {
	  set = new ClassGlobSet(new ClassGlob_Commalist());
	  set.addElement(one.get_classglob());
	}
	if (!set.contains(host)) set.addElement(host);
      }
    }}
    return {{ one == null ? null :
              set == null ? (ClassGlobSpec) one : (ClassGlobSpec) set }}
  }

  /** Return a GlobSet that matches all the edges in the GlobSpec,
      or null if there are none. */
  GlobSet collectEdgeGlobs() to EdgeGlob {
    before EdgeGlob {{
      if (return_val == null) return_val = new GlobSet();
      return_val.addElement(host);
    }}
  }

  /** Merge two glob specs into the union of the two. */
  GlobSpec union(GlobSpec spec) {{
    if (spec == null) return this;
    return spec.collectGlobs(collectGlobs(new GlobSet()));
  }}
  /** Destructively append all globs to the set, and return it. */
  GlobSet collectGlobs(GlobSet globset) = allGlobs {
    before Glob {{ globset.addElement(host); }}
    return {{ globset }}
  }
}

ClassGlobSpec {
  GlobSpec toGlobSpec() = allGlobs {
    {{ OneGlob one; GlobSet set; }}
    before ClassGlob {{
      if (one == null) {
	one = new OneGlob(host);
      } else {
	if (set == null) {
	  set = new GlobSet();
	  set.addElement(one.get_glob());
	}
	set.addElement(host);
      }
    }}
    return {{ one == null ? (GlobSpec) new GlobSet() :
	      set == null ? (GlobSpec) one : (GlobSpec) set }}
  }
}


// Repetition utility functions -- these should probably be generated
// automatically.

ClassGlobSet {
  void addElement(ClassGlob glob) {{ globs.addElement(glob); }}
}
GlobSet {
  void addElement(Glob glob) {{
    if (globs == null) globs = new Glob_Commalist();
    globs.addElement(glob);
  }}
}

ClassGlobSpec { abstract Enumeration elements(); }
ClassGlobSet { Enumeration elements() {{ return globs.elements(); }} }
OneClassGlob {
  Enumeration elements() {{
    return ClassGlobSet.parse("{" + classglob + "}").elements();
  }}
}

ClassGlobSet {
  void setadd(ClassGlobSpec spec) {{
    Enumeration e = spec.elements();
    while (e.hasMoreElements()) {
      ClassGlob glob = (ClassGlob) e.nextElement();
      if (!contains(glob)) addElement(glob);
    }
  }}
  boolean contains(ClassGlob glob) {{ return globs.contains(glob); }}
}