gt:       if (has(xml,"color")) view.nodeColor.put(x, parseDotColor(xml));
context: /* 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.io.File;
  import java.io.IOException;
  import java.io.PrintWriter;
  import java.io.StringWriter;
  import java.util.ArrayList;
  import java.util.List;
  import java.util.Map;
  import java.util.Set;
  import java.util.TreeMap;
  import java.util.TreeSet;
  import edu.mit.csail.sdg.alloy4.Util;
  import edu.mit.csail.sdg.alloy4.XMLNode;
  import edu.mit.csail.sdg.alloy4graph.DotColor;
  import edu.mit.csail.sdg.alloy4graph.DotPalette;
  import edu.mit.csail.sdg.alloy4graph.DotShape;
  import edu.mit.csail.sdg.alloy4graph.DotStyle;
  
  /** This utility class contains methods to read and write VizState customizations.
   *
   * <p><b>Thread Safety:</b> Can be called only by the AWT event thread.
   */
  
  public final class StaticThemeReaderWriter {
  
     /** Constructor is private, since this utility class never needs to be instantiated. */
     private StaticThemeReaderWriter() { }
  
     /** Read the XML file and merge its settings into an existing VizState object. */
     public static void readAlloy(String filename, VizState theme) throws IOException {
        File file = new File(filename);
        try {
           XMLNode elem = new XMLNode(file);
           for(XMLNode sub: elem.getChildren("view")) parseView(sub,theme);
        } catch(Throwable e) {
           throw new IOException("The file \""+file.getPath()+"\" is not a valid XML file, or an error occurred in reading.");
        }
     }
  
     /** Write the VizState's customizations into a new file (which will be overwritten if it exists). */
     public static void writeAlloy(String filename, VizState theme) throws IOException {
        PrintWriter bw = new PrintWriter(filename,"UTF-8");
        bw.write("<?xml version=\"1.0\"?>\n<alloy>\n\n");
        if (theme!=null) {
           try {
              writeView(bw, theme);
           } catch(IOException ex) {
              Util.close(bw);
              throw new IOException("Error writing to the file \""+filename+"\"");
           }
        }
        bw.write("\n</alloy>\n");
        if (!Util.close(bw)) throw new IOException("Error writing to the file \""+filename+"\"");
     }
  
     /*============================================================================================*/
  
     /** Does nothing if the element is malformed. */
     private static void parseView(final XMLNode x, VizState now) {
        /*
         * <view orientation=".." nodetheme=".." edgetheme=".." hidePrivate="yes/no" hideMeta="yes/no" useOriginalAtomNames="yes/no" fontsize="12">
         *   <projection> .. </projection>
         *   <defaultnode../>
         *   <defaultedge../>
         *   0 or more NODE or EDGE
         * </view>
         */
        if (!x.is("view")) return;
        for(XMLNode xml:x) {
           if (xml.is("projection")) {
              now.deprojectAll();
              for(AlloyType t:parseProjectionList(now,xml)) now.project(t);
           }
        }
        if (has(x,"useOriginalAtomNames")) now.useOriginalName(getbool(x,"useOriginalAtomNames"));
        if (has(x,"hidePrivate")) now.hidePrivate(getbool(x,"hidePrivate"));
        if (has(x,"hideMeta")) now.hideMeta(getbool(x,"hideMeta"));
        if (has(x,"fontsize")) now.setFontSize(getint(x,"fontsize"));
        if (has(x,"nodetheme")) now.setNodePalette(parseDotPalette(x,"nodetheme"));
        if (has(x,"edgetheme")) now.setEdgePalette(parseDotPalette(x,"edgetheme"));
        for(XMLNode xml:x) {
           if (xml.is("defaultnode")) parseNodeViz(xml, now, null);
           else if (xml.is("defaultedge")) parseEdgeViz(xml, now, null);
           else if (xml.is("node")) {
              for(XMLNode sub:xml.getChildren("type")) {
                 AlloyType t=parseAlloyType(now,sub); if (t!=null) parseNodeViz(xml, now, t);
              }
              for(XMLNode sub:xml.getChildren("set")) {
                 AlloySet s=parseAlloySet(now,sub); if (s!=null) parseNodeViz(xml, now, s);
              }
           }
           else if (xml.is("edge")) {
              for(XMLNode sub:xml.getChildren("relation")) {
                 AlloyRelation r=parseAlloyRelation(now,sub); if (r!=null) parseEdgeViz(xml, now, r);
              }
           }
        }
     }
  
     /*============================================================================================*/
  
     /** Writes nothing if the argument is null. */
     private static void writeView(PrintWriter out, VizState view) throws IOException {
        if (view==null) return;
        VizState defaultView=new VizState(view.getOriginalInstance());
        out.write("<view");
        writeDotPalette(out, "nodetheme", view.getNodePalette(), defaultView.getNodePalette());
        writeDotPalette(out, "edgetheme", view.getEdgePalette(), defaultView.getEdgePalette());
        if (view.useOriginalName()!=defaultView.useOriginalName()) {
           out.write(" useOriginalAtomNames=\"");
           out.write(view.useOriginalName() ? "yes" : "no");
           out.write("\"");
        }
        if (view.hidePrivate()!=defaultView.hidePrivate()) {
           out.write(" hidePrivate=\"");
           out.write(view.hidePrivate() ? "yes" : "no");
           out.write("\"");
        }
        if (view.hideMeta()!=defaultView.hideMeta()) {
           out.write(" hideMeta=\"");
           out.write(view.hideMeta() ? "yes" : "no");
           out.write("\"");
        }
        if (view.getFontSize()!=defaultView.getFontSize()) {
           out.write(" fontsize=\""+view.getFontSize()+"\"");
        }
        out.write(">\n");
        if (view.getProjectedTypes().size()>0) writeProjectionList(out, view.getProjectedTypes());
        out.write("\n<defaultnode" + writeNodeViz(view, defaultView, null));
        out.write("/>\n\n<defaultedge" + writeEdgeViz(view, defaultView, null));
        out.write("/>\n");
        // === nodes ===
        Set<AlloyNodeElement> types = new TreeSet<AlloyNodeElement>();
        types.addAll(view.getOriginalModel().getTypes());
        types.addAll(view.getCurrentModel().getTypes());
        types.addAll(view.getOriginalModel().getSets());
        types.addAll(view.getCurrentModel().getSets());
        Map<String,Set<AlloyNodeElement>> viz2node=new TreeMap<String,Set<AlloyNodeElement>>();
        for(AlloyNodeElement t:types) {
           String str=writeNodeViz(view,defaultView,t);
           Set<AlloyNodeElement> nodes=viz2node.get(str);
           if (nodes==null) viz2node.put(str, nodes=new TreeSet<AlloyNodeElement>());
           nodes.add(t);
        }
        for(Map.Entry<String,Set<AlloyNodeElement>> e:viz2node.entrySet()) {
           out.write("\n<node"+e.getKey()+">\n");
           for(AlloyNodeElement ts:e.getValue()) {
              if (ts instanceof AlloyType) writeAlloyType(out,(AlloyType)ts);
              else if (ts instanceof AlloySet) writeAlloySet(out,(AlloySet)ts);
           }
           out.write("</node>\n");
        }
        // === edges ===
        Set<AlloyRelation> rels = new TreeSet<AlloyRelation>();
        rels.addAll(view.getOriginalModel().getRelations());
        rels.addAll(view.getCurrentModel().getRelations());
        Map<String,Set<AlloyRelation>> viz2edge=new TreeMap<String,Set<AlloyRelation>>();
        for(AlloyRelation r:rels) {
           String str=writeEdgeViz(view,defaultView,r);
           if (str.length()==0) continue;
           Set<AlloyRelation> edges=viz2edge.get(str);
           if (edges==null) viz2edge.put(str, edges=new TreeSet<AlloyRelation>());
           edges.add(r);
        }
        for(Map.Entry<String,Set<AlloyRelation>> e:viz2edge.entrySet()) {
           out.write("\n<edge"+e.getKey()+">\n");
           for(AlloyRelation r:e.getValue()) writeAlloyRelation(out,r);
           out.write("</edge>\n");
        }
        // === done ===
        out.write("\n</view>\n");
     }
  
     /*============================================================================================*/
  
     /** Return null if the element is malformed. */
     private static AlloyType parseAlloyType(VizState now, XMLNode x) {
        /* class AlloyType implements AlloyNodeElement {
         *      String name;
         * }
         * <type name="the type name"/>
         */
        if (!x.is("type")) return null;
        String name=x.getAttribute("name");
        if (name.length()==0) return null; else return now.getCurrentModel().hasType(name);
     }
  
     /** Writes nothing if the argument is null. */
     private static void writeAlloyType(PrintWriter out, AlloyType x) throws IOException {
        if (x!=null) Util.encodeXMLs(out, "   <type name=\"", x.getName(), "\"/>\n");
     }
  
     /*============================================================================================*/
  
     /** Return null if the element is malformed. */
     private static AlloySet parseAlloySet(VizState now, XMLNode x) {
        /* class AlloySet implements AlloyNodeElement {
         *   String name;
         *   AlloyType type;
         * }
         * <set name="name" type="name"/>
         */
        if (!x.is("set")) return null;
        String name=x.getAttribute("name"), type=x.getAttribute("type");
        if (name.length()==0 || type.length()==0) return null;
        AlloyType t=now.getCurrentModel().hasType(type);
        if (t==null) return null; else return now.getCurrentModel().hasSet(name, t);
     }
  
     /** Writes nothing if the argument is null. */
     private static void writeAlloySet(PrintWriter out, AlloySet x) throws IOException {
        if (x!=null) Util.encodeXMLs(out,"   <set name=\"",x.getName(),"\" type=\"",x.getType().getName(),"\"/>\n");
     }
  
     /*============================================================================================*/
  
     /** Return null if the element is malformed. */
     private static AlloyRelation parseAlloyRelation(VizState now, XMLNode x) {
        /*
         * <relation name="name">
         *   2 or more <type name=".."/>
         * </relation>
         */
        List<AlloyType> ans=new ArrayList<AlloyType>();
        if (!x.is("relation")) return null;
        String name=x.getAttribute("name");
        if (name.length()==0) return null;
        for(XMLNode sub:x.getChildren("type")) {
           String typename=sub.getAttribute("name");
           if (typename.length()==0) return null;
           AlloyType t = now.getCurrentModel().hasType(typename);
           if (t==null) return null;
           ans.add(t);
        }
        if (ans.size()<2) return null; else return now.getCurrentModel().hasRelation(name, ans);
     }
  
     /** Writes nothing if the argument is null. */
     private static void writeAlloyRelation(PrintWriter out, AlloyRelation x) throws IOException {
        if (x==null) return;
        Util.encodeXMLs(out, "   <relation name=\"", x.getName(), "\">");
        for(AlloyType t:x.getTypes()) Util.encodeXMLs(out, " <type name=\"", t.getName(), "\"/>");
        out.write(" </relation>\n");
     }
  
     /*============================================================================================*/
  
     /** Always returns a nonnull (though possibly empty) set of AlloyType. */
     private static Set<AlloyType> parseProjectionList(VizState now, XMLNode x) {
        /*
         * <projection>
         *   0 or more <type name=".."/>
         * </projection>
         */
        Set<AlloyType> ans=new TreeSet<AlloyType>();
        if (x.is("projection")) for(XMLNode sub:x.getChildren("type")) {
           String name=sub.getAttribute("name");
           if (name.length()==0) continue;
           AlloyType t = now.getOriginalModel().hasType(name);
           if (t!=null) ans.add(t);
        }
        return ans;
     }
  
     /** Writes an empty Projection tag if the argument is null or empty */
     private static void writeProjectionList(PrintWriter out, Set<AlloyType> types) throws IOException {
        if (types==null || types.size()==0) { out.write("\n<projection/>\n"); return; }
        out.write("\n<projection>");
        for(AlloyType t:types) Util.encodeXMLs(out, " <type name=\"", t.getName(), "\"/>");
        out.write(" </projection>\n");
     }
  
     /*============================================================================================*/
  
     /** Do nothing if the element is malformed; note: x can be null. */
     private static void parseNodeViz(XMLNode xml, VizState view, AlloyNodeElement x) {
        /*
         * <node visible="inherit/yes/no"  label=".."  color=".."  shape=".."  style=".."
         * showlabel="inherit/yes/no"  showinattr="inherit/yes/no"
         * hideunconnected="inherit/yes/no" nubmeratoms="inherit/yes/no">
         *      zero or more SET or TYPE
         * </node>
         *
         * Each attribute, if omitted, means "no change".
         * Note: BOOLEAN is tristate.
         */
        if (has(xml,"visible"))         view.nodeVisible.put     (x, getbool(xml, "visible"));
        if (has(xml,"hideunconnected")) view.hideUnconnected.put (x, getbool(xml, "hideunconnected"));
        if (x==null || x instanceof AlloySet) {
           AlloySet s=(AlloySet)x;
           if (has(xml,"showlabel"))  view.showAsLabel.put (s, getbool(xml, "showlabel"));
           if (has(xml,"showinattr")) view.showAsAttr.put  (s, getbool(xml, "showinattr"));
        }
        if (x==null || x instanceof AlloyType) {
           AlloyType t=(AlloyType)x;
           if (has(xml,"numberatoms"))  view.number.put (t, getbool(xml, "numberatoms"));
        }
        if (has(xml,"style")) view.nodeStyle.put(x, parseDotStyle(xml));
  