/* AbstrTraversal.java * Bryan Chadwick :: 2007 * Abstract traversal function */ package edu.neu.ccs.demeterf; import java.lang.reflect.*; import java.util.ArrayList; import edu.neu.ccs.demeterf.util.*; import edu.neu.ccs.demeterf.lib.List; /** Abstract Traversal with Control. Concrete traversals * ({@link edu.neu.ccs.demeterf.Traversal Traversal} and * {@link edu.neu.ccs.demeterf.parallel.ParTraversal ParTraversal}) * complete the implementation, providing sequential and parallel traversal of * data structures respectively. */ public abstract class AbstTraversal{ protected Control control; /** Create a Traversal that goes Everywhere */ public AbstTraversal(){ this(Control.everywhere()); } /** Create a Traversal with Selective edge/field Bypassing */ public AbstTraversal(Control c){ control = c; } /** Do the Traversal... No traversal arguments */ public Ret traverse(Object o){ return this.traverse(o, Option.none()); } /** Do the Traversal... With a traversal argument */ public Ret traverse(Object o, Object a){ return this.traverse(o, Option.some(a)); } /** Indentation amount for nested comments */ protected static int indent = 0; /** Computes the indentation string for nested comments */ protected static String indent(){ return indent(indent); } /** Computes an indentation string of i spaces. */ protected static String indent(int i){ if(i==0)return ""; return " "+indent(i-1); } /** Do the Traversal... With an ML like Option argument (Some/None)*/ protected Ret traverse(Object o, Option arg){ indent++; Object result = null; if(o == null){ if(!Util.allowNull)Util.nullError(); return null; } Class c = o.getClass(); boolean hasArg = arg.some(); List fl = Util.getFuncFields(c); if(control.isBuiltIn(c)){ result = applyBuilder(Util.addArg(new Object[]{o},arg),true); }else{ if(c.isArray()){ result = traverseArray((Object[])o, arg); }else{ ArrayList ret = new ArrayList(); ret.add(o); for(Field f : fl){ if(!control.ignore(c, f.getName())){ try{ f.setAccessible(true); Object tret = f.get(o); if(tret == null){ // Throw an error if(!Util.allowNull)Util.nullFieldError(f); }else{ Option farg = (hasArg)?applyAugment(new Object[]{o, arg.get()},f):arg; if(!control.skip(c, f.getName())) tret = this.traverse(tret, farg); } ret.add(tret); }catch(Exception e){ throw (RuntimeException)e; } } } if(hasArg)ret.add(arg.get()); result = applyBuilder(ret.toArray(),false); } } indent--; return (Ret)result; } /** Traverses each element of an Array, and combines the results into * an array with an element type of the 'closest' common supertype * of all returned values */ protected Object traverseArray(Object[] o, Option arg){ ArrayList ret = new ArrayList(); Class c = Object.class; for(int i = 0; i < o.length; i++){ Object t = traverse(o[i], arg); Class tc = t.getClass(); ret.add(t); if(i == 1)c = tc; else while(!c.isAssignableFrom(tc)) c = c.getSuperclass(); } return ret.toArray((Object[])Array.newInstance(c, 0)); } /** Apply the Augmentor to the Argument at this Object before * traversing the given field. */ protected Option applyAugment(Object o[], Field f){ return Option.some(applyAugment(o, f.getDeclaringClass(), f.getName())); } /** Apply the Builder to this list of 'fields' */ protected abstract Object applyBuilder(Object o[], boolean prim); /** Apply the Augmentor the the traversal argument at this object * before traversing the given 'field' */ protected abstract Object applyAugment(Object o[], Class pc, String fn); }