package edu.neu.ccs.demeter.dj;

import java.util.*;

/**
 * A compact, efficient representation of a set of paths through a
 * class graph that can be used to traverse an object graph.
 */
public class Traversal extends edu.neu.ccs.demeter.aplib.Traversal {
  /** The DJ version string. */
  public static String getVersion() { return ClassGraph.getVersion(); }

  Strategy strategy;
  edu.neu.ccs.demeter.aplib.Traversal traversal;
  
  /**
   * Compute the traversal determined by s and cg.
   *
   * @throws TraversalException if the resulting traversal is
   * empty.
   */
  public Traversal(Strategy s, ClassGraph cg) {
    super(cg);
    strategy = s;
    try {
      traversal = compute(s, cg);
    } catch (edu.neu.ccs.demeter.aplib.TraversalException e) {
      throw new TraversalException(e.getMessage());
    }
  }

  /**
   * Compute the traversal determined by strategy s and cg.
   *
   * @throws TraversalException if the resulting traversal is
   * empty.
   */
  public Traversal(String s, ClassGraph cg) {
    this(new Strategy(s), cg);
  }

  /**
   * The strategy expression used to compute the traversal.
   */
  public Strategy getStrategy() { return strategy; }

  /**
   * The slice of the object graph rooted at o determined by the
   * traversal.
   */
  public ObjectGraphSlice slice(Object o)
  { return new ObjectGraphSlice(o, this); }

  /**
   * Traverse the object graph rooted at o according to the traversal,
   * visiting v at each node and returning the value of
   * v.getReturnValue() at the end of the traversal.
   */
  public Object traverse(Object o, Visitor v)
  { return slice(o).traverse(v); }

  /**
   * Traverse the object graph rooted at o according to the traversal,
   * visiting the visitors in array v in sequence at each node and
   * returning the value of v[0].getReturnValue() at the end of the
   * traversal.
   */
  public Object traverse(Object o, Visitor v[])
  { return slice(o).traverse(v); }

  /**
   * Fetch the object in the object graph rooted at o corresponding
   * to the target(s) of the traversal.
   */
  public Object fetch(Object o) throws FetchException
  { return slice(o).fetch(); }

  /**
   * Gather into a list the objects in the object graph rooted at o
   * corresponding to the target(s) of the traversal.
   */
  public List gather(Object o)
  { return slice(o).gather(); }

  /**
   * A fixed-size List backed by the object graph rooted at o.  The
   * elements of the list are the objects reachable by the strategy in
   * the object graph with the given root whose class is a target of
   * the strategy.  Note that this list (like the List returned by
   * Arrays.asList, but unlike the List returned by gather()) is
   * write-through, i.e. modifying it will modify the underlying
   * object graph, and vice versa.
   *
   * @throws TraversalSourceException if the type of o is not a
   * source of the strategy.
   */
  public java.util.List asList(Object o) {
    return slice(o).asList();
  }

  /**
   * Make a class graph with just the classes and edges in the
   * traversal.
   */
  public ClassGraph toClassGraph() {
    return new ClassGraph(this);
  }

  // Delegation stuff, to implement the parent's abstract methods.
  // This is sort of like the Proxy and Decorator patterns, although
  // the purpose is to subclass all the classes in a Composite pattern.

  public List getNodeSets() {
    return traversal.getNodeSets();
  }

  public NodeSet getNodeSet(Object v) {
    return traversal.getNodeSet(v);
  }

  public List getStartSet() {
    return traversal.getStartSet();
  }

  public NodeSet getStartSet(Object v) {
    return traversal.getStartSet(v);
  }

  public List getFinishSet() {
    return traversal.getFinishSet();
  }

  public NodeSet getFinishSet(Object v) {
    return traversal.getFinishSet(v);
  }

  public List getEdgeSets() {
    return traversal.getEdgeSets();
  }

  public EdgeSet getEdgeSet(String key) {
    return traversal.getEdgeSet(key);
  }

  public String toString() {
    return traversal.toString();
  }

  public boolean equals(Object t) {
    return traversal.equals(t);
  }

  public int hashCode() {
    return traversal.hashCode();
  }
}
