/* $Id: ListSet.java 17813 2010-01-12 18:33:21Z linus $ ***************************************************************************** * Copyright (c) 2009 Contributors - see below * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * tfmorris ***************************************************************************** * * Some portions of this file was previously release using the BSD License: */ // Copyright (c) 1996-2007 The Regents of the University of California. All // Rights Reserved. Permission to use, copy, modify, and distribute this // software and its documentation without fee, and without a written // agreement is hereby granted, provided that the above copyright notice // and this paragraph appear in all copies. This software program and // documentation are copyrighted by The Regents of the University of // California. The software program and documentation are supplied "AS // IS", without any accompanying services from The Regents. The Regents // does not warrant that the operation of the program will be // uninterrupted or error-free. The end-user understands that the program // was developed for research purposes and is advised not to rely // exclusively on the program for any reason. IN NO EVENT SHALL THE // UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR DIRECT, INDIRECT, // SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST PROFITS, // ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF // THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF // SUCH DAMAGE. THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY // WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE // PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF // CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, // UPDATES, ENHANCEMENTS, OR MODIFICATIONS. package org.argouml.cognitive; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Enumeration; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.ListIterator; import java.util.Set; /** * An Ordered, non-duplicated collection of objects (not exactly a * mathematical set because it is ordered). * * @param <T> The type of objects this ListSet is to contain. */ public class ListSet<T extends Object> implements Serializable, Set<T>, List<T> { private static final int TC_LIMIT = 50; private List<T> list; /** * A hash set containing the same items as the list so that we can * use it for fast lookups. */ private Set<T> set; /** * The mutex/lock which is used for operations that need to check/modify * both the set and list. Get operations which only access the list can * rely on the fact that it is a synchronized list. */ private final Object mutex = new Object(); /** * The constructor. */ public ListSet() { list = Collections.synchronizedList(new ArrayList<T>()); set = new HashSet<T>(); } /** * The constructor. * * @param n the initial capacity of the ListSet */ public ListSet(int n) { list = Collections.synchronizedList(new ArrayList<T>(n)); set = new HashSet<T>(n); } /** * The constructor. * * @param o1 the first object to add */ public ListSet(T o1) { list = Collections.synchronizedList(new ArrayList<T>()); set = new HashSet<T>(); add(o1); } /** * @param iter an enumeration of objects to be added */ public void addAllElements(Enumeration<T> iter) { while (iter.hasMoreElements()) { add(iter.nextElement()); } } /** * @param iter an iterator of objects to be added */ public void addAllElements(Iterator<T> iter) { while (iter.hasNext()) { add(iter.next()); } } /** * @param iter an iterator of objects to be added * @param p the predicate the objects have to fulfill to be added */ public void addAllElementsSuchThat(Iterator<T> iter, org.argouml.util.Predicate p) { if (p instanceof org.argouml.util.PredicateTrue) { addAllElements(iter); } else { while (iter.hasNext()) { T e = iter.next(); if (p.evaluate(e)) { add(e); } } } } /** * @param s a listset of objects to be added * @param p the predicate the objects have to fulfill to be added */ public void addAllElementsSuchThat(ListSet<T> s, org.argouml.util.Predicate p) { synchronized (s.mutex()) { addAllElementsSuchThat(s.iterator(), p); } } /* * @see java.util.Collection#remove(java.lang.Object) */ public boolean remove(Object o) { synchronized (mutex) { boolean result = contains(o); if (o != null) { list.remove(o); set.remove(o); } return result; } } /** * @param o the object to be removed */ public void removeElement(Object o) { if (o != null) { list.remove(o); } } /** * Remove all objects. */ public void removeAllElements() { clear(); } /* * @see java.util.Collection#contains(java.lang.Object) */ public boolean contains(Object o) { synchronized (mutex) { if (o != null) { return set.contains(o); } } return false; } /** * @param p the predicate the objects have to fulfill * @return true if at least one object in the listset fulfills the predicate */ public boolean containsSuchThat(org.argouml.util.Predicate p) { return findSuchThat(p) != null; } /** * Return the first object that causes the given predicate to return * true. * * @param p the predicate the objects have to fulfill * @return the found object or null */ public Object findSuchThat(org.argouml.util.Predicate p) { synchronized (list) { for (Object o : list) { if (p.evaluate(o)) { return o; } } } return null; } /* * @see java.lang.Object#hashCode() * * This will result in rather bad performance but at least we will * not violate the contract together with {@link #equals(Object)}. */ @Override public int hashCode() { return 0; } @Override public boolean equals(Object o) { if (!(o instanceof ListSet)) { return false; } ListSet set = (ListSet) o; if (set.size() != size()) { return false; } synchronized (list) { for (Object obj : list) { if (!(set.contains(obj))) { return false; } } } return true; } /* * @see java.util.Collection#size() */ public int size() { return list.size(); } @Override public String toString() { StringBuilder sb = new StringBuilder("Set{"); synchronized (list) { for (Iterator it = iterator(); it.hasNext();) { sb.append(it.next()); if (it.hasNext()) { sb.append(", "); } } } sb.append("}"); return sb.toString(); } /** * Reply the Set of all objects that can be reached from the receiving Set * by taking steps defined by the given ChildGenerator. The result includes * the elements of the original Set. In order to avoid very deep searches * which are often programming mistakes, only paths of length TC_LIMIT or * less are considered. * * @param cg the given childgenerator * @return the resulting listset */ public ListSet<T> transitiveClosure(org.argouml.util.ChildGenerator cg) { return transitiveClosure(cg, TC_LIMIT, org.argouml.util.PredicateTrue.getInstance()); } /** * Reply the Set of all objects that can be reached from the receiving Set * by taking steps defined by the given ChildGenerator. The result DOES NOT * include the elements of the original Set. In order to avoid very deep * searches which are often programming mistakes, only paths of length * TC_LIMIT or less are considered. * * @param cg the given childgenerator * @return the resulting listset */ public ListSet<T> reachable(org.argouml.util.ChildGenerator cg) { return reachable(cg, TC_LIMIT, org.argouml.util.PredicateTrue.getInstance()); } /** * Reply the Set of all objects that can be reached from the receiving Set * by taking steps defined by the given ChildGenerator. The result DOES NOT * include the elements of the original Set. In order to avoid very deep * searches which are often programming mistakes, only paths of given max * length or less are considered. Only paths consisting of elements which * all cause predicate.evaluate() to return true are considered. * * @param cg the given childgenerator * @param max the maximum depth * @param predicate the predicate the objects have to fulfill * @return the resulting listset */ public ListSet<T> reachable(org.argouml.util.ChildGenerator cg, int max, org.argouml.util.Predicate predicate) { ListSet<T> kids = new ListSet<T>(); synchronized (list) { for (Object r : list) { kids.addAllElementsSuchThat(cg.childIterator(r), predicate); } } return kids.transitiveClosure(cg, max, predicate); } /** * Reply the Set of all objects that can be reached from the receiving Set * by taking steps defined by the given ChildGenerator. The result includes * the elements of the original Set. In order to avoid very deep searches * which are often programming mistakes, only paths of given max length or * less are considered. Only paths consisting of elements which all cause * predicate.evaluate() to return true are considered. * * @param cg the given childgenerator * @param max the maximum depth * @param predicate the predicate the objects have to fulfill * @return the resulting listset */ public ListSet<T> transitiveClosure(org.argouml.util.ChildGenerator cg, int max, org.argouml.util.Predicate predicate) { int iterCount = 0; int lastSize = -1; ListSet<T> touched = new ListSet<T>(); ListSet<T> frontier; ListSet<T> recent = this; touched.addAll(this); while ((iterCount < max) && (touched.size() > lastSize)) { iterCount++; lastSize = touched.size(); frontier = new ListSet<T>(); synchronized (recent) { for (T recentElement : recent) { Iterator frontierChildren = cg.childIterator(recentElement); frontier.addAllElementsSuchThat(frontierChildren, predicate); } } touched.addAll(frontier); recent = frontier; } return touched; } /* * @see java.util.Collection#isEmpty() */ public boolean isEmpty() { return list.isEmpty(); } /* * @see java.util.Collection#iterator() */ public Iterator<T> iterator() { return list.iterator(); } /** * @return mutex object to synchronize on for iteration */ public Object mutex() { return list; } /* * @see java.util.Collection#toArray() */ public Object[] toArray() { return list.toArray(); } /* * @see java.util.Collection#toArray(java.lang.Object[]) */ public <A> A[] toArray(A[] arg0) { return list.toArray(arg0); } /* * @see java.util.Collection#add(java.lang.Object) */ public boolean add(T arg0) { synchronized (mutex) { boolean result = set.contains(arg0); if (!result) { set.add(arg0); list.add(arg0); } return !result; } } /* * @see java.util.Collection#containsAll(java.util.Collection) */ public boolean containsAll(Collection arg0) { synchronized (mutex) { return set.containsAll(arg0); } } /* * @see java.util.Collection#addAll(java.util.Collection) */ public boolean addAll(Collection< ? extends T> arg0) { return list.addAll(arg0); } /* * @see java.util.Collection#retainAll(java.util.Collection) */ public boolean retainAll(Collection< ? > arg0) { return list.retainAll(arg0); } /* * @see java.util.Collection#removeAll(java.util.Collection) */ public boolean removeAll(Collection arg0) { boolean result = false; for (Iterator iter = arg0.iterator(); iter.hasNext();) { result = result || remove(iter.next()); } return result; } /* * @see java.util.Collection#clear() */ public void clear() { synchronized (mutex) { list.clear(); set.clear(); } } /* * @see java.util.List#addAll(int, java.util.Collection) */ public boolean addAll(int arg0, Collection< ? extends T> arg1) { return list.addAll(arg0, arg1); } /* * @see java.util.List#get(int) */ public T get(int index) { return list.get(index); } /* * @see java.util.List#set(int, java.lang.Object) */ public T set(int arg0, T o) { throw new UnsupportedOperationException("set() method not supported"); } /* * @see java.util.List#add(int, java.lang.Object) */ public void add(int arg0, T arg1) { synchronized (mutex) { if (!set.contains(arg1)) { list.add(arg0, arg1); } } } /* * @see java.util.List#remove(int) */ public T remove(int index) { synchronized (mutex) { T removedElement = list.remove(index); set.remove(removedElement); return removedElement; } } /* * @see java.util.List#indexOf(java.lang.Object) */ public int indexOf(Object o) { return list.indexOf(o); } /* * @see java.util.List#lastIndexOf(java.lang.Object) */ public int lastIndexOf(Object o) { return list.lastIndexOf(o); } /* * @see java.util.List#listIterator() */ public ListIterator<T> listIterator() { return list.listIterator(); } /* * @see java.util.List#listIterator(int) */ public ListIterator<T> listIterator(int index) { return list.listIterator(index); } /* * @see java.util.List#subList(int, int) */ public List<T> subList(int fromIndex, int toIndex) { return subList(fromIndex, toIndex); } }