/* 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; /** Immutable; represents an Alloy atom in an instance. * * <p><b>Thread Safety:</b> Can be called only by the AWT event thread. */ public final class AlloyAtom implements Comparable<AlloyAtom> { /** The original name of this atom from the original Kodkod or other analysis. */ private final String originalName; /** The most specific AlloyType that this atom belongs to. */ private final AlloyType type; /** The index is a number that differentiates atoms of the same AlloyType; * one special convention: (this atom is the only atom with this type) iff (index==Integer.MAX_VALUE) */ private final int index; /** Create a new AlloyAtom with the given type and index. */ public AlloyAtom(AlloyType type, int index) { this.type=type; this.index=index; this.originalName=type.getName()+"."+index; } /** Create a new AlloyAtom with the given type, index, and label. */ public AlloyAtom(AlloyType type, int index, String originalName) { this.type=type; this.index=index; this.originalName=originalName; } /** Return a label for this atom as recommended by a theme (theme can be null if there's no theme to consult). */ public String getVizName(VizState theme, boolean numberAtoms) { if (theme!=null) { if (theme.useOriginalName() || type.getName().equals("String")) return originalName; if (index==Integer.MAX_VALUE && type.getName().equals("Int") && theme.label.get(type).length()==0) { // Special handling for Meta Model. (Only meta model could have index==MAX_VALUE) return "Int"; } if (index==Integer.MIN_VALUE && type.getName().equals("seq/Int") && theme.label.get(type).length()==0) { // Special handling for Meta Model. (Only meta model could have index==MIN_VALUE) return "seq/Int"; } if (index==Integer.MAX_VALUE || !numberAtoms) return theme.label.get(type); else return theme.label.get(type)+index; } if (type.getName().equals("Int")) return ""+index; // Special override to display integers better if (type.getName().equals("seq/Int")) return ""+index; // Special override to display integers better if (index==Integer.MAX_VALUE || !numberAtoms) return type.getName(); else return type.getName()+index; } /** Return the type of the AlloyAtom. */ public AlloyType getType() { return type; } /** Provides a human-readable label for debugging purpose. */ @Override public String toString() { return getVizName(null,true); } /** Compare first by type, then by index, then by the original names. * <br> We guarantee x.equals(y) iff x.compareTo(y)==0 * * <p> As a special cosmetic enhancement: * if we're comparing integer atoms, we want to ignore the difference between seqInt and Int. */ public int compareTo(AlloyAtom otherAtom) { if (otherAtom==null) return 1; AlloyType at = type; if (at.equals(AlloyType.SEQINT)) at=AlloyType.INT; AlloyType bt = otherAtom.type; if (bt.equals(AlloyType.SEQINT)) bt=AlloyType.INT; // This renaming is necessary in order to make sure the comparison is transitive. // For example, assuming seq/Int comprises 0..3, then we want atom0 < atom5, // even though atom0's TYPENAME > atom5's TYPENAME. // On the other hand, if you have an atom X with type X, then we want to make sure X>5 just like X>0 // (even though lexically, the type name "X" < the type name "seq/Int" if (at.equals(AlloyType.INT) && bt.equals(AlloyType.INT)) return (index < otherAtom.index) ? -1 : ((index > otherAtom.index) ? 1 : 0); int result=at.compareTo(bt); if (result!=0) return result; // We don't want to use the "return (index-otherAtom.index);" trick, // especially since singleton sets will have index of Integer.MAX_VALUE. if (index != otherAtom.index) return (index < otherAtom.index)?-1:1; return originalName.compareTo(otherAtom.originalName); } /** Two AlloyAtoms are equal if they have the same type, same index, and same original name. */ @Override public boolean equals(Object other) { if (!(other instanceof AlloyAtom)) return false; if (other==this) return true; AlloyAtom otherAtom = (AlloyAtom)other; return index==otherAtom.index && type.equals(otherAtom.type) && originalName.equals(otherAtom.originalName); } /** Returns a hash code based on the type and index. */ @Override public int hashCode() { return 7*type.hashCode()+5*index+17*originalName.hashCode(); } }