/* Alloy Analyzer 4 -- Copyright (c) 2006-2009, Felix Chang * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files * (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ package edu.mit.csail.sdg.alloy4viz; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import edu.mit.csail.sdg.alloy4compiler.ast.Expr; import edu.mit.csail.sdg.alloy4compiler.ast.ExprHasName; import edu.mit.csail.sdg.alloy4compiler.ast.ExprVar; import edu.mit.csail.sdg.alloy4compiler.ast.Sig; import edu.mit.csail.sdg.alloy4compiler.ast.Sig.Field; import edu.mit.csail.sdg.alloy4compiler.translator.A4Solution; import edu.mit.csail.sdg.alloy4compiler.translator.A4Tuple; import edu.mit.csail.sdg.alloy4compiler.translator.A4TupleSet; import edu.mit.csail.sdg.alloy4.Err; import edu.mit.csail.sdg.alloy4.OurTree; import edu.mit.csail.sdg.alloy4.Pair; import edu.mit.csail.sdg.alloy4.Util; import static edu.mit.csail.sdg.alloy4.Util.encode; /** GUI tree that displays an instance as a tree. * * <p><b>Thread Safety:</b> Can be called only by the AWT event thread. */ public final class VizTree extends OurTree { /** {@inheritDoc} */ @Override public String convertValueToText(Object val, boolean selected, boolean expanded, boolean leaf, int row, boolean focus) { String c = ">"; if (onWindows) c = selected ? " style=\"color:#ffffff;\">" : " style=\"color:#000000;\">"; if (val instanceof A4Solution) return "<html> <b" + c + encode(title==null ? "" : title)+"</b></html>"; if (val instanceof Sig) { String label = ((Sig)val).label; if (label.startsWith("this/")) label = label.substring(5); return "<html> <b" + c + "sig " + encode(label) + "</b></html>"; } if (val instanceof ExprVar) return "<html> <b" + c + "set " + encode(((ExprVar)val).label) + "</b></html>"; if (val instanceof String) return "<html> <span" + c + encode((String)val) + "</span></html>"; if (val instanceof Pair) return "<html> <b" + c + "field " + encode(((ExprHasName)(((Pair<?,?>)val).b)).label) + "</b></html>"; if (val instanceof A4Tuple) { StringBuilder sb = new StringBuilder("<html> <span" + c); A4Tuple tp = (A4Tuple) val; for(int i=1; i<tp.arity(); i++) { if (i>1) sb.append(" -> "); sb.append(encode(tp.atom(i))); } sb.append("</span></html>"); return sb.toString(); } return ""; } /** {@inheritDoc} */ @Override public Object do_root() { return instance; } /** {@inheritDoc} */ @Override public List<?> do_ask(Object parent) { List<Object> ans = new ArrayList<Object>(); try { if (parent instanceof A4Solution) { return toplevel; } else if (parent instanceof Sig || parent instanceof ExprVar) { A4TupleSet ts = (A4TupleSet) (instance.eval((Expr)parent)); for(A4Tuple t: ts) ans.add(t.atom(0)); } else if (parent instanceof String) { String atom = (String)parent; for(Sig s: instance.getAllReachableSigs()) for(Field f: s.getFields()) for(A4Tuple t: instance.eval(f)) { if (t.atom(0).equals(atom)) { ans.add(new Pair<String,ExprHasName>(atom, f)); break; } } for(ExprVar f: instance.getAllSkolems()) if (f.type().arity()>1) for(A4Tuple t: (A4TupleSet)(instance.eval(f))) { if (t.atom(0).equals(atom)) { ans.add(new Pair<String,ExprHasName>(atom, f)); break; } } } else if (parent instanceof Pair) { Pair<?,?> p = (Pair<?,?>)parent; ExprHasName rel = (ExprHasName) (p.b); String atom = (String) (p.a); for(A4Tuple tuple: (A4TupleSet) (instance.eval(rel))) if (tuple.atom(0).equals(atom)) { if (tuple.arity()==2) ans.add(tuple.atom(1)); else ans.add(tuple); } } else if (parent instanceof A4Tuple) { A4Tuple tp = (A4Tuple)parent; for(int i=1; i<tp.arity(); i++) if (!ans.contains(tp.atom(i))) ans.add(tp.atom(i)); return ans; // we don't want to sort this; we want the original order } Collections.sort(ans, new Comparator<Object>() { public int compare(Object a, Object b) { String t1, t2; if (a instanceof Pair) { t1=((ExprHasName)(((Pair<?,?>)a).b)).label; t2=((ExprHasName)(((Pair<?,?>)b).b)).label; } else { t1=a.toString(); t2=b.toString(); } int i = t1.compareToIgnoreCase(t2); if (i!=0) return i; else return t1.compareTo(t2); } }); return ans; } catch(Err er) { return ans; } } /** This ensures the class can be serialized reliably. */ private static final long serialVersionUID = 0; /** Caches whether we're on Windows or not. */ private final boolean onWindows; /** The title of this tree. */ private final String title; /** The instance being displayed. */ private final A4Solution instance; /** The list of toplevel nodes to show. */ private final List<Object> toplevel; /** Constructs a tree to display the given instance. */ public VizTree(A4Solution instance, String title, int fontSize) { super(fontSize); this.instance = instance; this.title = title; this.onWindows = Util.onWindows(); ArrayList<Object> toplevel = new ArrayList<Object>(); for(Sig s: instance.getAllReachableSigs()) if (s!=Sig.UNIV && s!=Sig.SEQIDX && s!=Sig.NONE) toplevel.add(s); for(ExprVar v: instance.getAllSkolems()) if (v.type().arity()==1 && v.label.startsWith("$")) toplevel.add(v); Collections.sort(toplevel, new Comparator<Object>() { public int compare(Object a, Object b) { String t1, t2; if (a instanceof Sig) { t1=((Sig)a).label; if (b instanceof ExprVar) return -1; else t2=((Sig)b).label; } else { t1=((ExprVar)a).label; if (b instanceof Sig) return 1; else t2=((ExprVar)b).label; } return Util.slashComparator.compare(t1, t2); } }); this.toplevel = Collections.unmodifiableList(toplevel); do_start(); } }