/* * Kodkod -- Copyright (c) 2005-present, Emina Torlak * * 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 kodkod.instance; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import kodkod.util.collections.Containers; /** * <p>Represents an ordered set of unique atoms. Objects used as atoms <b>must</b> properly * implement {@link java.lang.Object#equals equals} and {@link java.lang.Object#hashCode hashCode} * methods. The behavior of a universe is not specified if the value of an object is changed in a * manner that affects equals comparisons while the object is an atom in the universe.</p> * * <p>Each universe provides a {@link kodkod.instance.TupleFactory tuple factory} * to facilitate creation of {@link kodkod.instance.Tuple tuples} and * {@link kodkod.instance.TupleSet sets of tuples} based on the atoms in the universe.</p> * * <p><b>Implementation Note:</b> although the atoms in a universe are not interpreted in any * way by the Kodkod engine, it is strongly recommended that the atoms * of the same 'type' be grouped together. For instance, suppose that a client model is specified in * terms of disjoint types Person = {Person0, Person1, Person2} and Dog = {Dog0, Dog1}. Then, * the client may observe an improvement in performance if he constructs the universe over the * sequences {Person0, Person1, Person2, Dog0, Dog1} or {Dog0, Dog1, Person0, Person1, Person2} rather * than any other permutation of these five atoms. * * @specfield size: int * @specfield atoms: [0..size)->one Object * @specfield factory: TupleFactory * @invariant factory = (TupleFactory<:universe).this * @invariant size > 0 * @author Emina Torlak */ public final class Universe implements Iterable<Object> { private final Object[] atoms; private final Map<Object,Integer> indices; private final TupleFactory factory; /** * Constructs a new Universe consisting of the given atoms, in the order that they are returned * by the specified Collection's Iterator. * * @ensures this.size' = atoms.size && this.atoms' = atoms.iterator * @throws NullPointerException atoms = null * @throws IllegalArgumentException atoms contains duplicates * @throws IllegalArgumentException atoms is empty */ public Universe(Collection<?> atoms) { if (atoms.isEmpty()) throw new IllegalArgumentException("Cannot create an empty universe."); this.atoms = new Object[atoms.size()]; this.indices = new HashMap<Object, Integer>(); int i = 0; for (Object atom : atoms) { if (indices.containsKey(atom)) throw new IllegalArgumentException(atom + " appears multiple times."); indices.put(atom, i); this.atoms[i] = atom; i++; } this.factory = new TupleFactory(this); } /** * Constructs a new Universe consisting of the given atoms, in the order that they appear * in the specified array * * @ensures this.size' = atoms.length && this.atoms' = atoms * @throws NullPointerException atoms = null * @throws IllegalArgumentException atoms contains duplicates * @throws IllegalArgumentException atoms is empty */ public Universe(Object...atoms) { if (atoms.length==0) throw new IllegalArgumentException("Cannot create an empty universe."); this.atoms = Containers.copy(atoms, new Object[atoms.length]); this.indices = new HashMap<Object, Integer>(); for (int i = 0; i < atoms.length; i++) { if (indices.containsKey(atoms[i])) throw new IllegalArgumentException(atoms[i] + " appears multiple times."); indices.put(atoms[i], i); } this.factory = new TupleFactory(this); } /** // * Returns a universe that stores the given atoms explicitly, in the specified order. // * The returned universe provides constant time index and atom lookup. // * @ensures { u: Universe | u.size = atoms.length && u.atoms = atoms } // * @throws IllegalArgumentException atoms contains duplicates // * @throws IllegalArgumentException atoms is empty // */ // public static Universe universe(Object...atoms) { return new ExplicitUniverse(atoms); } // // /** // * Returns a universe that stores the given atoms explicitly, in the order in which they // * are returned by the collection's iterator. The returned universe provides constant // * time index and atom lookup. // * @ensures { u: Universe | u.size = atoms.size() && u.atoms[int] = atoms } // * @throws IllegalArgumentException atoms contains duplicates // * @throws IllegalArgumentException atoms is empty // */ // public static Universe universe(Collection<?> atoms) { return new ExplicitUniverse(atoms); } /** * Returns a TupleFactory that can be used to construct tuples and sets * of tuples based on this universe. * @return this.factory */ public TupleFactory factory() { return this.factory; } /** * Returns the size of this universe * * @return #this.atoms */ public int size() { return atoms.length; } /** * Returns true if atom is in this universe, otherwise returns false. * * @return atom in this.atoms[int] */ public boolean contains(Object atom) { return indices.containsKey(atom); } /** * Returns the i_th atom in this universe * * @return this.atoms[i] * @throws IndexOutOfBoundsException i < 0 || i >= this.size */ public Object atom(int index) { if (index<0||index>=atoms.length) throw new IndexOutOfBoundsException("Invalid universe index: " + index); return atoms[index]; } /** * Returns the index of the specified atom in this universe; if the * atom is not in this universe, an IllegalArgumentException is thrown. * * @return this.atoms.atom * @throws IllegalArgumentException no this.atoms.atom */ public int index(Object atom) { if (indices.containsKey(atom)) return indices.get(atom); else throw new IllegalArgumentException("No such atom in the universe: " + atom); } /** * Returns an iterator over atoms in this universe, according to their * order in the universe. * @return an iterator over atoms in this universe, according to their * order in the universe */ public Iterator<Object> iterator() { return Containers.iterate(atoms); } /** * Returns a string representation of this universe. * @return string representation of this universe. */ public String toString() { return Arrays.toString(atoms); } }