package checker; import java.io.File; import java.util.Enumeration; import java.util.Iterator; import java.util.List; import java.util.ListIterator; import java.util.jar.JarFile; import edu.neu.ccs.demeter.Ident; import edu.neu.ccs.demeter.aplib.Traversal; import edu.neu.ccs.demeter.aplib.Traversal.EdgeSet; import edu.neu.ccs.demeter.aplib.cd.ClassGraph; import edu.neu.ccs.demeter.aplib.sg.ClassDict; import edu.neu.ccs.demeter.aplib.sg.GraphNodes; import edu.neu.ccs.demeter.aplib.sg.NodeSubsetExpression; import edu.neu.ccs.demeter.aplib.sg.NodeSubsetSpec; import edu.neu.ccs.demeter.aplib.sg.SelectorLanguage; import edu.neu.ccs.demeter.aplib.sg.SelectorName; import edu.neu.ccs.demeter.aplib.sg.StrategyDef; import edu.neu.ccs.demeter.dj.Visitor; public class SemanticChecker { private static final String pkg = "edu.neu.ccs.demeter.aplib.sg"; private SelectorLanguage sl; private edu.neu.ccs.demeter.dj.ClassGraph slCg; private ClassGraphPair cgPair; // lists for storing the defined Strategy and NodeSubset // names List definedStrategyNames; List definedNodeSubsetNames; private edu.neu.ccs.demeter.dj.ClassGraph getClassGraph () { edu.neu.ccs.demeter.dj.ClassGraph cg = new edu.neu.ccs.demeter.dj.ClassGraph (true, false); String classPath = System.getProperty ("java.class.path"); String [] paths = classPath.split (File.pathSeparator); int lastSlash = paths [0].lastIndexOf ("\\"); String sgPath = paths [0].substring (0, lastSlash) + "\\plugins\\DJ-Eclipse-1.0\\DJ.jar"; try { JarFile jf = new JarFile (new File (sgPath)); Enumeration e = jf.entries (); while (e.hasMoreElements ()) { String entry = e.nextElement ().toString (); if (entry.indexOf ("sg") != -1 && entry.indexOf ("class") != -1) { entry = entry.replace ('/', '.'); int lastDot = entry.lastIndexOf (".class"); entry = entry.substring(0, lastDot); Class c = Class.forName (entry); cg.addClass (c); } } } catch (Exception e) { System.out.println (e.getMessage ()); } return cg; } private edu.neu.ccs.demeter.aplib.cd.ClassGraph getClassDictionaryClassGraph () throws Exception { // get the ClassDict object from the SelectorLanguage ClassDict cd = (ClassDict) slCg.traverse (sl, "from " + pkg + ".SelectorLanguage to " + pkg + ".ClassDict", new CdVisitor ()); try { // create a ClassGraph using the text of the class dictionary edu.neu.ccs.demeter.aplib.cd.ClassGraph cdCg = ClassGraph.fromString (cd.get_text ().toString ()); // return the Normalized class dictionary ClassGraph component of // the ClassGraphPair return cdCg; } catch (Exception e) { throw new Exception ("Details: the class dictionary contains syntax errors!", e); } } private List getDefinedStrategyNames () throws Exception { List names = slCg.gather (sl, "from " + pkg + ".SelectorLanguage " + "through " + pkg + ".StrategyLanguage " + "bypassing " + pkg + ".StrategyExpression " + "via " + pkg + ".StrategyName " + "to edu.neu.ccs.demeter.Ident" ); return names; } private List getDefinedNodeSubsetNames () throws Exception { List names = slCg.gather (sl, "from " + pkg + ".SelectorLanguage " + "through " + pkg + ".NodeSubsetLanguage " + "bypassing " + pkg + ".NodeSubsetExpression " + "via " + pkg + ".NodeSubsetName " + "to edu.neu.ccs.demeter.Ident" ); return names; } private List getUsedStrategyNamesFromNodes () throws Exception { List names = slCg.gather (sl, "from " + pkg + ".SelectorLanguage " + "through " + pkg + ".NodeSubsetLanguage " + "via " + pkg + ".StrategyName " + "to edu.neu.ccs.demeter.Ident" ); return names; } private List getUsedNodeSubsetNames () throws Exception { List names = slCg.gather (sl, "from " + pkg + ".SelectorLanguage " + "through " + pkg + ".NodeSubsetExpression " + "via " + pkg + ".NodeSubsetName " + "to edu.neu.ccs.demeter.Ident" ); return names; } private List getStrategies () throws Exception { List strats = slCg.gather (sl, "from " + pkg + ".SelectorLanguage " + "through " + pkg + ".StrategyLanguage " + "via " + pkg + ".StrategyExpression " + "to " + pkg + ".Strategy" ); return strats; } private edu.neu.ccs.demeter.aplib.sg.Strategy getStrategyByName (String name) throws Exception { edu.neu.ccs.demeter.aplib.sg.Strategy s = (edu.neu.ccs.demeter.aplib.sg.Strategy) slCg.traverse (sl, "from " + pkg + ".SelectorLanguage " + "through " + pkg + ".StrategyLanguage " + "to " + pkg + ".StrategyDef", new StrategyVisitor (name) ); return s; } private NodeSubsetExpression getNodeSubsetExpressionByName (String name) throws Exception { NodeSubsetExpression n = (NodeSubsetExpression) slCg.traverse (sl, "from " + pkg + ".SelectorLanguage " + "through " + pkg + ".NodeSubsetLanguage " + "to " + pkg + ".NodeSubsetSpec", new NodeSubsetVisitor (name) ); return n; } private void checkStrategyLanguage () throws Exception { // get the strategy names that are used in the node subset language List usedNodeStratNames = getUsedStrategyNamesFromNodes (); // compare the sets of strategy names - make sure that each strategy // name used has been defined if (! definedStrategyNames.containsAll (usedNodeStratNames)) throw new Exception ("Details: not all used StrategyNames are defined " + "in the StrategyLanguage!"); // get all of the strategies from the strategy language List strategies = getStrategies (); ListIterator iter = strategies.listIterator (); // try to create a traversal from each of the strategies, // using the normalized class dictionary ClassGraph while (iter.hasNext ()) { try { Traversal t = Traversal.compute (new edu.neu.ccs.demeter.dj.Strategy (iter.next ().toString ()), cgPair.getNormalized ()); } catch (Exception e) { // failed to create a Traversal for the Strategy - generate an error // and throw the exception throw new Exception ("Details: one or more of the Strategies defined " + "in the StrategyLanguage refer to ClassNames that do not exist in " + "the class dictionary!", e); } } } private void checkNodeSubsetLanguage () throws Exception { // get the NodeSubset names that are used in other NodeSubsets List usedNames = getUsedNodeSubsetNames (); // make sure all of the used NodeSubset names are defined if (! definedNodeSubsetNames.containsAll (usedNames)) throw new Exception ("Details: not all used NodeSubsetNames are " + "defined in the NodeSubsetLanguage!"); // check for other types of semantic errors in // the more complex node subset expressions... } private SelectorName getAndCheckSelector () throws Exception { // get the name of the strategy or node subset that is specified by // the SelectorLanguage's Selector SelectorName selector = (SelectorName) slCg.traverse (sl, "from " + pkg + ".SelectorLanguage " + "to " + pkg + ".SelectorName", new SelectorVisitor () ); // check the selector against the list of strategy and node // subset names if (! definedStrategyNames.contains (selector.get_ident ()) && ! definedNodeSubsetNames.contains (selector.get_ident ())) throw new Exception ("Details: the Selector refers to an " + "undefined Strategy or NodeSubset!"); // selector checks out - return it return selector; } private SemanticChecker () { } protected void setClassGraphs () throws Exception { // check the class dictionary and get its ClassGraph edu.neu.ccs.demeter.aplib.cd.ClassGraph cdCg = getClassDictionaryClassGraph (); // set the normalized ClassGraph component of the ClassGraphPair cgPair.setNormalized (cdCg.normalize ()); // set contents of the lists of Strategy and NodeSubset name // definitions definedStrategyNames = getDefinedStrategyNames (); definedNodeSubsetNames = getDefinedNodeSubsetNames (); // check the strategy language semantics checkStrategyLanguage (); // check the node subset language semantics checkNodeSubsetLanguage (); // check the selector for semantic errors and store a copy // of it SelectorName sn = getAndCheckSelector (); String sName = sn.get_ident ().toString (); // Strategy that will be defined depending on the type of // selector that was specified edu.neu.ccs.demeter.aplib.sg.Strategy s = null; // check whether the selector is a NodeSubset or Strategy if (definedStrategyNames.contains (sn.get_ident ())) { // if the selector refers to a Strategy, get its corresponding // Strategy component s = getStrategyByName (sName); } else if (definedNodeSubsetNames.contains (sn.get_ident ())) { // if the selector refers to a NodeSubset, get its corresponding // NodeSubsetSpec component NodeSubsetExpression n = getNodeSubsetExpressionByName (sName); // currently, only NodeSubsets of class GraphNodes are supported if (n instanceof GraphNodes) { // get the StrategyName from the GraphNodes object Ident strat = (Ident) slCg.traverse (n, "from " + pkg + ".NodeSubsetExpression " + "through " + pkg + ".Simple " + "via " + pkg + ".GraphNodes " + "to edu.neu.ccs.demeter.Ident", new IdentVisitor () ); // get the strategy that is associated with the StrategyName // contained in the GraphNodes s = getStrategyByName (strat.toString ()); } else throw new Exception ("Details: currently, only GraphNodes " + "are supported!"); } else throw new Exception ("Details: could not locate the Selector!"); try { // use the Strategy component and existing ClassGraph object // to compute a Traversal Traversal t = Traversal.compute (s, cdCg); // create a new ClassGraph for storing the reduced // class dictionary ClassGraph ClassGraph newCg = new ClassGraph (); // use the Traversal to construct the nodes and edges of the // new (reduced) ClassGraph for (Iterator it = t.getEdgeSets ().iterator (); it.hasNext (); ) newCg.addEdge (((EdgeSet) it.next ()).getEdge ()); // set the reduced ClassGraph component of the ClassGraphPair cgPair.setReduced (newCg); } catch (Exception e) { // there was some problem creating the Traversal - signal // an error and throw the new exception throw new Exception ("Details: failed to create a reduced " + "ClassGraph using the provided selector!", e); } } public SemanticChecker (SelectorLanguage sl) throws Exception { this.sl = sl; // create a ClassGraph for the SelectorLanguage slCg = getClassGraph (); // initialize the ClassGraphPair - its fields will be set following // the semantic checks cgPair = new ClassGraphPair (); // perform the semantic checks on the provided SelectorLanguage try { setClassGraphs (); } catch (Exception e) { throw new Exception ("Error! Provided SelectorLanguage is semantically incorrect!", e); } } public ClassGraphPair getClassGraphs () { return cgPair; } class IdentVisitor extends Visitor { private Ident i; public void before (Ident host) { i = host; } public Object getReturnValue () { return i; } } class StrategyVisitor extends Visitor { private String sName; private edu.neu.ccs.demeter.aplib.sg.Strategy s; public StrategyVisitor (String n) { sName = n; } public void before (StrategyDef host) { // get the visited StrategyDef's StrategyName Ident hostName = (Ident) slCg.traverse (host, "from " + pkg + ".StrategyDef " + "bypassing " + pkg + ".StrategyExpression " + "via " + pkg + ".StrategyName " + "to edu.neu.ccs.demeter.Ident", new IdentVisitor () ); // if the host's name matches that of the Visitor, get the // host's embedded Strategy from its StrategyExpression // component if (sName.equals (hostName.toString ())) { s = (edu.neu.ccs.demeter.aplib.sg.Strategy) slCg.traverse (host, "from " + pkg + ".StrategyDef " + "through " + pkg + ".StrategyExpression " + "to " + pkg + ".Strategy", new Visitor () { private edu.neu.ccs.demeter.aplib.sg.Strategy strat; public void before (edu.neu.ccs.demeter.aplib.sg.Strategy host) { strat = host; } public Object getReturnValue () { return strat; } } ); } } public Object getReturnValue () { return s; } } class NodeSubsetVisitor extends Visitor { private String name; private NodeSubsetExpression n; public NodeSubsetVisitor (String n) { name = n; } public void before (NodeSubsetSpec host) { // get the visited NodeSubsetSpec's NodeSubsetName Ident hostName = (Ident) slCg.traverse (host, "from " + pkg + ".NodeSubsetSpec " + "bypassing " + pkg + ".NodeSubsetExpression " + "via " + pkg + ".NodeSubsetName " + "to edu.neu.ccs.demeter.Ident", new IdentVisitor () ); // if the host's name matches that of the Visitor, get // the host's embedded NodeSubsetExpression if (name.equals (hostName.toString ())) { n = (NodeSubsetExpression) slCg.traverse (host, "from " + pkg + ".NodeSubsetSpec " + "to " + pkg + ".NodeSubsetExpression", new Visitor () { private NodeSubsetExpression n; public void before (NodeSubsetExpression host) { n = host; } public Object getReturnValue () { return n; } } ); } } public Object getReturnValue () { return n; } } class SelectorVisitor extends Visitor { private SelectorName sn; public void before (SelectorName host) { sn = host; } public Object getReturnValue () { return sn; } } class CdVisitor extends Visitor { private ClassDict cd; public void before (ClassDict host) { cd = host; } public Object getReturnValue () { return cd; } } }