/* 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.alloy4compiler.sim; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.IOException; import java.lang.ref.WeakReference; import java.util.WeakHashMap; /** Immutable; represents an atom. * * <p> Outside of this class, we guarantee for any SimAtom x and y, then "x.equals(y) iff x==y". * <br> Even though that means "equals()" and "==" are equivalent, * <br> the equals() method is much slower than "==" for SimAtom, * <br> so you should always try to use "==" on SimAtom. * * <p><b>Thread Safety:</b> Safe. */ public final class SimAtom { /** This map is used to canonicalize the atoms. */ private static final WeakHashMap<SimAtom,WeakReference<SimAtom>> map = new WeakHashMap<SimAtom,WeakReference<SimAtom>>(); /** The String label for the atom; all distinct atoms have distinct labels. */ private String string; /** Construct a SimAtom; this constructor must only be called by make() since we want to canonicalize all SimAtom instances out there. */ private SimAtom(String x) { this.string = x; } /** Construct a SimAtom for the given label, or if an existing SimAtom hasn't been garbage collected yet then return that instead. */ public static SimAtom make(String label) { synchronized(map) { SimAtom x = new SimAtom(label); WeakReference<SimAtom> ans = map.get(x); if (ans != null) { SimAtom y = ans.get(); if (y!=null) return y; } map.put(x, new WeakReference<SimAtom>(x)); return x; } } /** Construct a SimAtom for the given integer, or if an existing SimAtom hasn't been garbage collected yet then return that instead. */ public static SimAtom make(int i) { return make(String.valueOf(i)); } /** Construct a SimAtom for the given integer, or if an existing SimAtom hasn't been garbage collected yet then return that instead. */ public static SimAtom make(long i) { return make(String.valueOf(i)); } /** Preconstructed atom representing emptystring. */ public static final SimAtom EMPTYSTRING = make(""); /** Preconstructed atom representing 0. */ public static final SimAtom ZERO = make("0"); /** Preconstructed atom representing 1. */ public static final SimAtom ONE = make("1"); /** Write this atom as "..". */ void write(BufferedOutputStream out) throws IOException { byte array[] = string.getBytes("UTF-8"); out.write('\"'); for(int n=array.length, i=0; i<n; i++) { byte b = array[i]; if (b=='\n') { out.write('\\'); out.write('n'); } else if (b=='\"') { out.write('\\'); out.write(b); } else if (b>0 && b<=' ') out.write(' '); else out.write(b); } out.write('\"'); } /** Read a "..." atom assuming the leading " has already been consumed. */ static SimAtom read(BufferedInputStream in) throws IOException { byte temp[] = new byte[64]; // to ensure proper detection of out-of-memory error, this number must be 2^n for some n>=0 int n = 0; while(true) { int c = in.read(); if (c<0) throw new IOException("Unexpected EOF"); if (c=='\"') break; if (c=='\\') { c=in.read(); if (c<0) throw new IOException("Unexpected EOF"); if (c=='n') c='\n'; } while (n >= temp.length) { byte temp2[] = new byte[temp.length * 2]; System.arraycopy(temp, 0, temp2, 0, temp.length); temp = temp2; } temp[n]=(byte)c; n++; } return make(new String(temp, 0, n, "UTF-8")); } /** If the atom starts with "-" or "0-9" then convert it into a 32-bit int (here we assume that it came from a 32-bit int) * <p> * If the atom does not start with "-" or "0-9", then return defaultValue. */ public Integer toInt(Integer defaultValue) throws NumberFormatException { int ans=0, i=0, n=string.length(); if (n==0) return defaultValue; // Due to Java's 2's complement arithmetic, this will successfully // convert all integers ranging from Integer.MIN to Integer.MAX if (string.charAt(0)=='-') i++; if (string.charAt(i)<'0' || string.charAt(i)>'9') return defaultValue; for(;i<n;i++) ans = ans*10 + (string.charAt(i) - '0'); if (string.charAt(0)=='-') return (-ans); else return ans; } /** Return the product of this atom and that atom. */ public SimTuple product(SimAtom that) { return SimTuple.make(this, that); } /** {@inheritDoc} */ @Override public boolean equals(Object that) { if (this==that) return true; if (!(that instanceof SimAtom)) return false; SimAtom x = (SimAtom)that; if (string==x.string) return true; else if (string.equals(x.string)) {string=x.string; return true;} else return false; // Change it so we share the same String; this is thread safe since String objects are immutable // so it doesn't matter if some thread sees the old String and some sees the new String. // JLS 3rd Edition 17.7 guarantees that writes and reads of references are atomic though not necessarily visible, // so another thread will either see the old String or the new String. } /** {@inheritDoc} */ @Override public int hashCode() { return string.hashCode(); } /** {@inheritDoc} */ @Override public String toString() { return string; } }