//Nicholas Cassie - ncassie
//Matt Finkel - mfinkel
// Jason Lacouture - ateamjay
// Project - phase 2
// March 15, 2004
// We have completed lab exercises 1,2,6,7,8,18

// sg.beh -- behavior for creating strategy graphs
// $Id: sg.beh,v 1.16 2003/01/30 02:26:04 dougo Exp $
Strategy {
  /** Read a strategy expression from a byte stream. */
  public static Strategy readFrom(InputStream in) throws ParseException {{
    return readFrom(in, new HashMap());
  }}
  /** Read a strategy expression from a char stream. */
  public static Strategy readFrom(Reader in) throws ParseException {{
    return readFrom(in, new HashMap());
  }}
  /** Convert a string to a strategy. */
  public static Strategy fromString(String s) {{
    return fromString(s, new HashMap());
  }}

  /**
   * Read a strategy expression from a byte stream.
   * Strategy references are resolved by looking them up in the
   * provided environent.
   */
  public static Strategy readFrom(InputStream in, Map env)
    throws ParseException
  {{
    Strategy s =  StrategyExpression.parse(in).get_strategy();
    s.attachEnv(env);
    return s;
  }}
  /**
   * Read a strategy expression from a char stream.
   * Strategy references are resolved by looking them up in the
   * provided environent.
   */
  public static Strategy readFrom(Reader in, Map env)
    throws ParseException
  {{
    Strategy s = StrategyExpression.parse(in).get_strategy();
    s.attachEnv(env);
    return s;
  }}
  /**
   * Convert a string to a strategy.
   * Strategy references are resolved by looking them up in the
   * provided environent.
   */
  public static Strategy fromString(String in, Map env) {{
    Strategy s = StrategyExpression.parse(in).get_strategy();
    s.attachEnv(env);
    return s;
  }}
}

Strategy {
  void attachEnv(Map env) to StrategyReference {
    before StrategyReference {{ host.env = env; }}
  }
}

StrategyGraph {
  {{
    List edgeList = new ArrayList();
    class IncidentEdges {
      List incoming = new ArrayList(), outgoing = new ArrayList();
    }
    Map nodes = new HashMap(); // node -> IncidentEdges
    Set sources = new HashSet(), targets = new HashSet();
    SymbolicNameMapI nameMap = null;
  }}
  init {{ edges = new SGEdge_SList(); }}
  StrategyGraph toGraph() {{
    if (edgeList.isEmpty()) {
      Enumeration e = edges.elements();
      int i = 0;
      Set allSources = new HashSet(), allTargets = new HashSet();
      while (e.hasMoreElements()) {
	SGEdge edge = (SGEdge) e.nextElement();
	addEdge(edge, i++);
	allSources.add(edge.get_source());
	allTargets.add(edge.get_target());
      }
      if (sources.isEmpty()) sources = allSources;
      if (targets.isEmpty()) targets = allTargets;
      if (nameMap == null)
	nameMap = (parsedNameMap == null ? new NameMap() : parsedNameMap);
      parsedNameMap = null;
    }
    return this;
  }}

  /* Add an edge to the strategy graph. */
  public void addEdge(SGEdge edge) {{
    addEdge(edge, edges.size());
    edges.addElement(edge);
  }}
  void addEdge(SGEdge edge, int i) {{
    edgeList.add(edge);

    GlobSpec source = edge.get_source();
    if (edge.isSource()) sources.add(source);
    IncidentEdges ie = (IncidentEdges) nodes.get(source);
    if (ie == null) nodes.put(source, ie = new IncidentEdges());
    ie.outgoing.add(new Integer(i));

    GlobSpec target = edge.get_target();
    if (edge.isTarget()) targets.add(target);
    ie = (IncidentEdges) nodes.get(target);
    if (ie == null) nodes.put(target, ie = new IncidentEdges());
    ie.incoming.add(new Integer(i));
  }}
}

PathDirective {
  {{ StrategyGraph graph; }}
  public StrategyGraph getGraph() {{
    if (graph == null) graph = toGraph();
    return graph;
  }}
}

PathDirective {
  StrategyGraph toGraph()
    to { From, NegativeConstraint, PositiveConstraint, To, ToStop, NameMap }
  {
    {{ SGEdge edge; }}
    {{ NameMap map = new NameMap(); }}
    before PathDirective {{
      return_val = new StrategyGraph();
      return_val.nameMap = map;
    }}
    before From {{
      ClassGlobSpec sourcespec = host.get_sources();
      edge = new SGEdge();
      edge.set_source(sourcespec.toGlobSpec());
      edge.set_sourcemarker(new SourceMarker());
    }}
    before NegativeConstraint {{
      edge.set_constraint(host);
    }}
    before PositiveConstraint {{
      GlobSpec glob = host.get_glob();
      edge.set_target(glob);
      return_val.addEdge(edge);
      edge = new SGEdge();
      edge.set_source(glob);
    }}
    before ToStop {{
      // Bypass the target(s).
      Bypassing constraint =
	Bypassing.parse("bypassing " + host.get_targets());
      edge.set_constraint(constraint.intersectWith(edge.get_constraint()));
    }}
    after TargetDirective {{
      ClassGlobSpec targets = host.get_targets();
      edge.set_target(targets.toGlobSpec());
      edge.set_targetmarker(new TargetMarker());
      return_val.addEdge(edge);
    }}
    before NameMap {{
      return_val.nameMap = NameMap.compose(host, map);
    }}
  }
}

Bypassing {
  NegativeConstraint intersectWith(NegativeConstraint constraint) {{
    if (constraint == null) return this;
    if (constraint instanceof OnlyThrough) return constraint;
    return new Bypassing(glob.union(constraint.get_glob()));
  }}
}

Translator {
{{
	edu.neu.ccs.demeter.aplib.cd.ClassGraph cg;
        edu.neu.ccs.demeter.dj.ClassGraph demCG;
	
	//constructor taking a demeter.aplib.cd.ClassGraph and a demeter.dj.ClassGraph
	public Translator(edu.neu.ccs.demeter.aplib.cd.ClassGraph origCD, edu.neu.ccs.demeter.dj.ClassGraph origDemCG) {
		cg = origCD;
                demCG = origDemCG;
	}

        
	//constructor taking a demeter.aplib.cd.ClassGraph
	public Translator(edu.neu.ccs.demeter.aplib.cd.ClassGraph origCD) {
		cg = origCD;
	}

	//constructor taking a demeter.dj.ClassGraph
        public Translator(edu.neu.ccs.demeter.dj.ClassGraph origCD){
              demCG = origCD;
        }
	
	//function to create the normalized version of the user's class dictionary
	public String normalizeCD()
	{
		return cg.normalize().toString();
	}

        public void printSelect(String travStrat)
        {
              edu.neu.ccs.demeter.aplib.cd.ClassGraph currCG = cg;
              
              edu.neu.ccs.demeter.aplib.sg.Strategy strat = edu.neu.ccs.demeter.aplib.sg.Strategy.fromString(travStrat);
 
              try{
                 edu.neu.ccs.demeter.aplib.Traversal t = edu.neu.ccs.demeter.aplib.Traversal.compute(strat, currCG);
 
                 edu.neu.ccs.demeter.aplib.cd.ClassGraph tempCG = new edu.neu.ccs.demeter.aplib.cd.ClassGraph();
                 for(Iterator it = t.getEdgeSets().iterator(); it.hasNext(); )
                    tempCG.addEdge(((edu.neu.ccs.demeter.aplib.Traversal.EdgeSet) it.next()).getEdge());
    
                 System.out.println("\nThe selected version of you class dictionary is:");
                 System.out.println(tempCG);
              }catch (Exception e){
                   System.out.println("Exception caught in Translator.getSelect");
             }
          
        }

        public String getStratExprString(SelectorLanguage s, GraphNodes gn)
        {

           edu.neu.ccs.demeter.dj.ClassGraph currCG = demCG;
           final String stratName = gn.get_strategyname().get_name().toString();

           String strStrat = "from * to *";
           edu.neu.ccs.demeter.dj.Visitor v = new edu.neu.ccs.demeter.dj.Visitor(){
               StrategyExpression expr = new StrategyExpression();
               void before(StrategyDef stratDef){
                   if(stratDef.get_strategyname().get_name().toString().equals(stratName)){
                       expr = stratDef.get_strategyexpression();
                   }
               }

               public Object getReturnValue(){
                  return expr;
               }
            };
               currCG.traverse(s, strStrat, v);

               StrategyExpression selectedStrategy = (StrategyExpression)v.getReturnValue();
               return selectedStrategy.get_strategy().toString();
         }
}}
}
SemanticChecker {
{{	
	edu.neu.ccs.demeter.dj.Strategy id;
        edu.neu.ccs.demeter.dj.ClassGraph cg;
	boolean isChecked;
        SelectorLanguage s;


	//constructor
	public SemanticChecker(edu.neu.ccs.demeter.dj.ClassGraph initClassGraph, SelectorLanguage sem)
	{
		id = new edu.neu.ccs.demeter.dj.Strategy("from edu.neu.ccs.demeter.aplib.sg.SelectorLanguage to *");
		cg = initClassGraph;
                isChecked = true;
                s = sem;
	}
	
	//call all the semantic checking functions of this class
	//returns true if semantics are proper, false otherwise
	public boolean checkSemantics()
	{
	        repUndef(cg);
                checkDefinedStrats(cg, getDefStrats(cg));	
               // checkTraversal(id, cg);
                return isChecked;
	}
	
	//get each used nodes and strategies, and check for their definition 
	void repUndef(final edu.neu.ccs.demeter.dj.ClassGraph cg){
		checkDefinedNodes(cg, getDefNodes(cg));
                checkDefinedStrats(cg, getDefStrats(cg));
	}
	
 	//create a hashSet of all the used nodeNames
	HashSet getDefNodes(final edu.neu.ccs.demeter.dj.ClassGraph cg)
	{
		String strategy = "from edu.neu.ccs.demeter.aplib.sg.SelectorLanguage bypassing edu.neu.ccs.demeter.aplib.sg.NodeSubsetExpression to edu.neu.ccs.demeter.aplib.sg.NodeSubsetName";
		edu.neu.ccs.demeter.dj.Visitor v = new edu.neu.ccs.demeter.dj.Visitor(){
			HashSet return_val = new HashSet();
			
			void before(NodeSubsetName nsn){
				return_val.add(nsn.get_name());
			}
								
			public Object getReturnValue(){
				return return_val;
			}
		};
		
		cg.traverse(s, strategy, v);
		return (HashSet)v.getReturnValue();
			
	}
 
	//create a hashSet of all the used Strategies
        HashSet getDefStrats(final edu.neu.ccs.demeter.dj.ClassGraph cg)
        {
                String strategy = "from edu.neu.ccs.demeter.aplib.sg.SelectorLanguage via edu.neu.ccs.demeter.aplib.sg.StrategyDef bypassing edu.neu.ccs.demeter.aplib.sg.StrategyExpression to edu.neu.ccs.demeter.aplib.sg.StrategyName";
                edu.neu.ccs.demeter.dj.Visitor v = new edu.neu.ccs.demeter.dj.Visitor(){
                        HashSet return_val = new HashSet();

                        void before(StrategyName sn){
                                return_val.add(sn.get_name());
                        }
                               
                        public Object getReturnValue(){
                                return return_val;
                        }
                };

                cg.traverse(s, strategy, v);
                return (HashSet)v.getReturnValue();

        }

	//check all the used NodeSubsetNames for definitions
	void checkDefinedNodes(final edu.neu.ccs.demeter.dj.ClassGraph cg, final HashSet classHash)
	{
		String usedThings = "from * through edu.neu.ccs.demeter.aplib.sg.NodeSubsetExpression to edu.neu.ccs.demeter.aplib.sg.NodeSubsetName";

		cg.traverse(s, usedThings, new edu.neu.ccs.demeter.dj.Visitor(){
			void before(NodeSubsetName nsn)
			{
				edu.neu.ccs.demeter.Ident vn = (edu.neu.ccs.demeter.Ident)cg.fetch(nsn, "from * to edu.neu.ccs.demeter.Ident");
				if(!classHash.contains(vn))
				{
					System.out.println("The object " + nsn.get_name().toString() + " is undefined.");
                                        isChecked = false;
				}
			}
		});
	}

	//check all the used Strategies for definitions
         void checkDefinedStrats(final edu.neu.ccs.demeter.dj.ClassGraph cg, final HashSet classHash1)
        {
                String usedThings = "from edu.neu.ccs.demeter.aplib.sg.SelectorLanguage to edu.neu.ccs.demeter.aplib.sg.StrategyName";

                cg.traverse(s, usedThings, new edu.neu.ccs.demeter.dj.Visitor(){
                        void before(StrategyName sn)
                        {
                                edu.neu.ccs.demeter.Ident vn = (edu.neu.ccs.demeter.Ident)cg.fetch(sn, "from * to edu.neu.ccs.demeter.Ident");
                                if(!classHash1.contains(vn))
                                {
                                        System.out.println("The object" + sn.get_name().toString() + "is undefined.");
                                        isChecked = false;
                                }
                        }
                });
        }
	
	//check the traversal
	void checkTraversal(String travStrat, final edu.neu.ccs.demeter.aplib.cd.ClassGraph cg)
	{
              try{

                 edu.neu.ccs.demeter.aplib.sg.Strategy strat = edu.neu.ccs.demeter.aplib.sg.Strategy.fromString(travStrat);
                 edu.neu.ccs.demeter.aplib.Traversal t = edu.neu.ccs.demeter.aplib.Traversal.compute(strat, cg);
 
                 edu.neu.ccs.demeter.dj.ClassGraph tempCG = new edu.neu.ccs.demeter.dj.ClassGraph();
                 for(Iterator it = t.getEdgeSets().iterator(); it.hasNext(); ){
                    tempCG.addEdge(((edu.neu.ccs.demeter.aplib.Traversal.EdgeSet) it.next()).getEdge());
                 }
                 edu.neu.ccs.demeter.dj.TraversalGraph tg = new edu.neu.ccs.demeter.dj.TraversalGraph(travStrat, tempCG);
              }catch (Exception e){
                   System.out.println("Exception caught in Translator.getSelect");
             }
	}	
}}
}

Main {
  /** Testing stub. */
  public static void main(String args[]) {{
    BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
    SelectorLanguage s = new SelectorLanguage();
    try {
      s = SelectorLanguage.parse(in);
    } catch (ParseException e) {
      System.err.println(e.getMessage());
    }
   //create the classGraph using the correct package
   edu.neu.ccs.demeter.dj.ClassGraph cg = new edu.neu.ccs.demeter.dj.ClassGraph("edu.neu.ccs.demeter.aplib.sg", true, false);
 
   //create a new semantic check with our cg and an object rooted at a SelectorLanguage object
   SemanticChecker semCheck = new SemanticChecker(cg, s);

   //checkSemantics returns a boolean
   //if it returns true the semantics have passed, otherwise there is an error in the user's input
   if(semCheck.checkSemantics())
   {

     //create a new visitor that gets the contents of the user's classdictionary
     edu.neu.ccs.demeter.dj.Visitor v = new edu.neu.ccs.demeter.dj.Visitor(){
	String classes = "";
	void before(ClassDict sn){
                classes = classes + sn.get_text();
	}
	public Object getReturnValue(){
		return classes;
	}
     };

     //get the class dictionary, store it in userCD
     cg.traverse(s, "from * to *", v);
    String userCD = (String)v.getReturnValue();

    //create a new classGraph from that class dictionary
    edu.neu.ccs.demeter.aplib.cd.ClassGraph userCG = edu.neu.ccs.demeter.aplib.cd.ClassGraph.fromString(userCD);

   System.out.println("The entered class dictionary was: \n" + userCG.toString());


    //translate the user's class dictionary into the two required outputs
    Translator cgTrans = new Translator(userCG, cg);

    //Normalized CD
    //System.out.println("\nThe normalized version of your Class Dictionary:");
    //System.out.println(cgTrans.normalizeCD());

    //Selected CD

    //create a visitor object to get all the selectorNames
    edu.neu.ccs.demeter.dj.Visitor selectorV = new edu.neu.ccs.demeter.dj.Visitor(){
       String selector = "";
       void before(SelectorName sn){
            selector = selector + sn.get_ident();
       }
       public Object getReturnValue(){
	    return selector;
       }
    };

   //get the selectorNames
   cg.traverse(s, "from * to *", selectorV);
   final String selectorName = (String)selectorV.getReturnValue();

    //create a visitor that checks each NodeSubsetSpec's for equality with our SelectorName
    //if you get a match, get the corresponding nodeSubsetExpression
    edu.neu.ccs.demeter.dj.Visitor nodeV = new edu.neu.ccs.demeter.dj.Visitor(){
        NodeSubsetExpression name;
        void before(NodeSubsetSpec nss){
	   if(selectorName.equals(nss.get_nodesubsetname().get_name().toString())){
               name = nss.get_nodesubsetexpression();
           }
        } 
        public Object getReturnValue(){
                return name;
        }
    };

    //get the nodeSubsetExpression
    cg.traverse(s, "from * to *", nodeV);


    String travStrat = cgTrans.getStratExprString(s, (GraphNodes)nodeV.getReturnValue());
    edu.neu.ccs.demeter.aplib.sg.Strategy strat = edu.neu.ccs.demeter.aplib.sg.Strategy.fromString(travStrat);
    semCheck.checkTraversal(travStrat, userCG);

    //Normalized CD
    System.out.println("\nThe normalized version of your Class Dictionary:");
    System.out.println(cgTrans.normalizeCD());

    //get the nodeSubsetExpression we're trying to work on if there is a selector name
    if (nodeV.getReturnValue() != null){
       cgTrans.printSelect(travStrat);
    }
    else
       System.out.println("No graph node found");
   } else
       System.out.println("The input semantics are incorrect, please try again.");
  }}
}