| /******************************************************************************* |
| * Copyright (c) 2006, 2012, 2011 IBM Corporation, Zeligsoft Inc., and others. |
| * 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: |
| * IBM - Initial API and implementation |
| * Zeligsoft - Bugs 244946, 248869 |
| * Axel Uhl (SAP AG) - Bug 342644 |
| *******************************************************************************/ |
| package org.eclipse.ocl.util; |
| |
| import java.math.BigDecimal; |
| import java.math.BigInteger; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.LinkedHashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.eclipse.ocl.Environment; |
| import org.eclipse.ocl.EvaluationEnvironment; |
| import org.eclipse.ocl.expressions.CollectionKind; |
| import org.eclipse.ocl.internal.OCLPlugin; |
| import org.eclipse.ocl.internal.evaluation.NumberUtil; |
| import org.eclipse.ocl.internal.l10n.OCLMessages; |
| import org.eclipse.ocl.types.CollectionType; |
| |
| /** |
| * Utility methods for working with OCL collection values. |
| * |
| * @author Christian W. Damus (cdamus) |
| */ |
| public class CollectionUtil { |
| |
| // not instantiable |
| private CollectionUtil() { |
| super(); |
| } |
| |
| /** |
| * Implementation of the OCL |
| * <tt>Collection::includes(object : T) : Boolean</tt> |
| * operation. |
| * |
| * @param self the source collection |
| * @param object an object |
| * @return whether the collection includes the object |
| */ |
| public static boolean includes(Collection<?> self, Object object) { |
| return self.contains(object); |
| } |
| |
| /** |
| * Implementation of the OCL |
| * <tt>Collection::excludes(object : T) : Boolean</tt> |
| * operation. |
| * |
| * @param self the source collection |
| * @param object an object |
| * @return whether the collection does not include the object |
| */ |
| public static boolean excludes(Collection<?> self, Object object) { |
| return !includes(self, object); |
| } |
| |
| /** |
| * Implementation of the OCL |
| * <tt>Collection::count(object : T) : Integer</tt> |
| * operation. |
| * |
| * @param self the source collection |
| * @param object an object |
| * @return the number of occurrences of the object in the collection |
| */ |
| public static int count(Collection<?> self, Object object) { |
| int count = 0; |
| for (Object next : self) { |
| if (ObjectUtil.equal(next, object)) { |
| count++; |
| } |
| } |
| return count; |
| } |
| |
| /** |
| * Implementation of the OCL |
| * <tt>Collection::includesAll(c : Collection(T)) : Boolean</tt> |
| * operation. |
| * |
| * @param self the source collection |
| * @param c another collection |
| * @return whether the source collection includes all of the elements |
| * of the other |
| */ |
| public static boolean includesAll(Collection<?> self, Collection<?> c) { |
| for (Object next : c) { |
| if (!includes(self, next)) { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| /** |
| * Implementation of the OCL |
| * <tt>Collection::excludesAll(c : Collection(T)) : Boolean</tt> |
| * operation. |
| * |
| * @param self the source collection |
| * @param c another collection |
| * @return whether the source collection does not contain any of the |
| * elements of the other |
| */ |
| public static boolean excludesAll(Collection<?> self, Collection<?> c) { |
| for (Object next : c) { |
| if (includes(self, next)) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| /** |
| * Implementation of the OCL |
| * <tt>Collection::isEmpty() : Boolean</tt> |
| * operation. |
| * |
| * @param self the source collection |
| * @return whether the collection does not have any elements |
| */ |
| public static boolean isEmpty(Collection<?> self) { |
| return self.isEmpty(); |
| } |
| |
| /** |
| * Implementation of the OCL |
| * <tt>Collection::max() : T</tt> |
| * operation. |
| * |
| * @param self the source collection |
| * @return the largest of the collection's elements |
| * @since 3.2 |
| */ |
| @SuppressWarnings("unchecked") |
| public static Object max(Collection<?> self) { |
| if (self.isEmpty()) { |
| return null; // undefined |
| } |
| Number maxVal = null; |
| for (Iterator<?> it = self.iterator(); it.hasNext();) { |
| Object object = it.next(); |
| if (!(object instanceof Number)) { |
| IllegalArgumentException error = new IllegalArgumentException(OCLMessages.MaxOperator_ERROR_); |
| OCLPlugin.throwing(CollectionUtil.class, "max", error);//$NON-NLS-1$ |
| throw error; |
| } |
| Number number = (Number)object; |
| if (maxVal == null) { |
| maxVal = number; |
| } |
| else { |
| maxVal = NumberUtil.commonPrecisionNumber(maxVal, number); |
| number = NumberUtil.commonPrecisionNumber(number, maxVal); |
| if (((Comparable<Number>)number).compareTo(maxVal) > 0) { |
| maxVal = number; |
| } |
| } |
| } |
| return NumberUtil.coerceNumber(maxVal); |
| } |
| |
| /** |
| * Implementation of the OCL |
| * <tt>Collection::min() : T</tt> |
| * operation. |
| * |
| * @param self the source collection |
| * @return the smallest of the collection's elements |
| * @since 3.2 |
| */ |
| @SuppressWarnings("unchecked") |
| public static Object min(Collection<?> self) { |
| if (self.isEmpty()) { |
| return null; // undefined |
| } |
| Number maxVal = null; |
| for (Iterator<?> it = self.iterator(); it.hasNext();) { |
| Object object = it.next(); |
| if (!(object instanceof Number)) { |
| IllegalArgumentException error = new IllegalArgumentException(OCLMessages.MinOperator_ERROR_); |
| OCLPlugin.throwing(CollectionUtil.class, "min", error);//$NON-NLS-1$ |
| throw error; |
| } |
| Number number = (Number)object; |
| if (maxVal == null) { |
| maxVal = number; |
| } |
| else { |
| maxVal = NumberUtil.commonPrecisionNumber(maxVal, number); |
| number = NumberUtil.commonPrecisionNumber(number, maxVal); |
| if (((Comparable<Number>)number).compareTo(maxVal) < 0) { |
| maxVal = number; |
| } |
| } |
| } |
| return NumberUtil.coerceNumber(maxVal); |
| } |
| |
| /** |
| * Implementation of the OCL |
| * <tt>Collection::notEmpty() : Boolean</tt> |
| * operation. |
| * |
| * @param self the source collection |
| * @return whether the collection has any elements |
| */ |
| public static boolean notEmpty(Collection<?> self) { |
| return !self.isEmpty(); |
| } |
| |
| /** |
| * Implementation of the OCL |
| * <tt>Collection::sum() : T</tt> |
| * operation. |
| * |
| * @param self the source collection |
| * @return the sum of the collection's elements |
| */ |
| public static Object sum(Collection<?> self) { |
| if (self.isEmpty()) { |
| return null; // undefined |
| } |
| Number sumVal = null; |
| for (Iterator<?> it = self.iterator(); it.hasNext();) { |
| Object object = it.next(); |
| if (!(object instanceof Number)) { |
| IllegalArgumentException error = new IllegalArgumentException(OCLMessages.SumOperator_ERROR_); |
| OCLPlugin.throwing(CollectionUtil.class, "sum", error);//$NON-NLS-1$ |
| throw error; |
| } |
| Number number = (Number)object; |
| if (sumVal == null) { |
| sumVal = number; |
| } |
| else { |
| sumVal = NumberUtil.commonPrecisionNumber(sumVal, number); |
| number = NumberUtil.commonPrecisionNumber(number, sumVal); |
| if (sumVal instanceof BigDecimal) { |
| sumVal = ((BigDecimal)sumVal).add((BigDecimal)number); |
| } |
| else if (sumVal instanceof BigInteger) { |
| sumVal = ((BigInteger)sumVal).add((BigInteger)number); |
| } |
| else if (sumVal instanceof Double) { |
| sumVal = (Double)sumVal + (Double)number; |
| } |
| else { |
| sumVal = (Long)sumVal + (Long)number; |
| } |
| } |
| } |
| return NumberUtil.coerceNumber(sumVal); |
| } |
| |
| /** |
| * Implementation of the OCL |
| * <ul> |
| * <li><tt>Set::=(set : Set(T)) : Boolean</tt></li> |
| * <li><tt>OrderedSet::=(set : OrderedSet(T)) : Boolean</tt></li> |
| * <li><tt>Bag::=(bag : Bag(T)) : Boolean</tt></li> |
| * <li><tt>Sequence::=(s : Sequence(T)) : Boolean</tt></li> |
| * </ul> |
| * operations. |
| * |
| * @param self the source collection |
| * @param c another collection of the same kind |
| * @return whether collections are equal |
| */ |
| public static boolean equals(Collection<?> self, Collection<?> c) { |
| if (self.size() != c.size()) { |
| // collections of different sizes cannot be equal |
| return false; |
| } else if (self instanceof Bag<?> && c instanceof Bag<?>) { |
| return ((Bag<?>) self).equals(c); |
| } else if (self instanceof List<?> && c instanceof List<?>) { |
| return ((List<?>) self).equals(c); |
| } else if (self instanceof LinkedHashSet<?> && c instanceof LinkedHashSet<?>) { |
| // OrderedSet |
| |
| // LinkedHashSet.equals() doesn't care about order but we do |
| int size1 = self.size(); |
| int size2 = c.size(); |
| if (size1 != size2) { |
| return false; |
| } |
| Iterator<?> it1 = self.iterator(); |
| Iterator<?> it2 = c.iterator(); |
| while (it1.hasNext()) { |
| Object o1 = it1.next(); |
| Object o2 = it2.next(); |
| if (!o1.equals(o2)) { |
| return false; |
| } |
| } |
| |
| return true; |
| } else if (self instanceof Set<?> && c instanceof Set<?>) { |
| return ((Set<?>) self).equals(c); |
| } else { |
| // incompatible OCL types |
| return false; |
| } |
| } |
| |
| /** |
| * Computes the hash of a collection, accounting for the similar hashing of |
| * primitive numeric values that OCL considers equal but Java does not. |
| * |
| * @param c a collection |
| * |
| * @return its hash |
| */ |
| public static int hashCode(Collection<?> c) { |
| int result = 1; |
| |
| for (Object next : c) { |
| result = 37 * result + ObjectUtil.hashCode(next); |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Implementation of the OCL |
| * <ul> |
| * <li><tt>Set::intersection(set : Set(T)) : Set(T)</tt></li> |
| * <li><tt>Set::intersection(bag : Bag(T)) : Set(T)</tt></li> |
| * <li><tt>Bag::intersection(set : Set(T)) : Set(T)</tt></li> |
| * <li><tt>Bag::intersection(bag : Bag(T)) : Set(T)</tt></li> |
| * </ul> |
| * operations. |
| * |
| * @param self the source set or bag |
| * @param c another set or bag |
| * @return the intersection of the source set or bag with the other set or bag |
| */ |
| public static <E> Collection<E> intersection( |
| Collection<? extends E> self, Collection<? extends E> c) { |
| |
| int size1 = self.size(); |
| int size2 = c.size(); |
| |
| // if either collection is empty, then so is the result |
| if (size1 == 0 || size2 == 0) { |
| if (self instanceof Set<?> || c instanceof Set<?>) { |
| return Collections.emptySet(); |
| } else { |
| return BagImpl.emptyBag(); |
| } |
| } |
| |
| Collection<E> result = null; |
| |
| if (self instanceof Set<?> || c instanceof Set<?>) { |
| // if either argument is a set, so is the result |
| if (size1 == 0 || size2 == 0) { |
| return Collections.emptySet(); |
| } |
| result = createNewSet(); |
| } else { |
| // both arguments are bags, so is the result |
| if (size1 == 0 || size2 == 0) { |
| return BagImpl.emptyBag(); |
| } |
| result = createNewBag(); |
| } |
| |
| // loop over the smaller collection and add only elements |
| // that are in the larger collection |
| if (self.size() > c.size()) { |
| for (E e : c) { |
| if (includes(self, e)) { |
| result.add(e); |
| } |
| } |
| } else { |
| for (E e : self) { |
| if (includes(c, e)) { |
| result.add(e); |
| } |
| } |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Implementation of the OCL |
| * <ul> |
| * <li><tt>Set::union(set : Set(T)) : Set(T)</tt></li> |
| * <li><tt>Set::union(bag : Bag(T)) : Bag(T)</tt></li> |
| * <li><tt>Bag::union(set : Set(T)) : Bag(T)</tt></li> |
| * <li><tt>Bag::union(bag : Bag(T)) : Bag(T)</tt></li> |
| * <li><tt>Sequence::union(s : Sequence(T)) : Sequence(T)</tt></li> |
| * </ul> |
| * operations. |
| * |
| * @param self the source collection |
| * @param c another collection |
| * @return the union of the source collection with the other |
| */ |
| public static <E> Collection<E> union( |
| Collection<? extends E> self, Collection<? extends E> c) { |
| |
| // if either argument is empty, then the union is the other, |
| // except the source is a set and the other is a bag in which |
| // case the result has to be a bag |
| if (self.isEmpty()) { |
| if (self instanceof Bag || c instanceof Bag) { |
| return createNewBag(c); |
| } else { |
| return createNewCollection(c); |
| } |
| } else if (c.isEmpty()) { |
| if (self instanceof Bag || c instanceof Bag) { |
| return createNewBag(self); |
| } else { |
| return createNewCollection(self); |
| } |
| } |
| |
| Collection<E> result = null; |
| if (self instanceof Bag<?> || c instanceof Bag<?>) { |
| result = createNewBag(self); |
| } else if (self instanceof List<?> || c instanceof List<?>) { |
| result = createNewSequence(self); |
| } else { |
| result = createNewSet(self); |
| } |
| |
| result.addAll(c); |
| |
| return result; |
| } |
| |
| /** |
| * Implementation of the OCL |
| * <ul> |
| * <li><tt>Set::flatten() : Set(T2)</tt></li> |
| * <li><tt>Bag::flatten() : Bag(T2)</tt></li> |
| * <li><tt>Sequence::flatten() : Sequence(T2)</tt></li> |
| * </ul> |
| * operations. |
| * |
| * @param self the source collection |
| * @return the flattened collection |
| */ |
| public static Collection<?> flatten(Collection<?> self) { |
| // Note: As OCL 2.3 (OMG 10-11-42) section A.2.5.8 fails to specify how to |
| // flatten an OrderedSet, we choose to flatten it into an OrderedSet |
| // represented by a LinkedHashSet. |
| Collection<?> result = self; |
| |
| for (;;) { |
| if (result.isEmpty()) { |
| break; |
| } |
| |
| Iterator<?> it = result.iterator(); |
| Object object = it.next(); |
| |
| // if the element type is not a collection type, the result is the |
| // current collection. |
| if (!(object instanceof Collection<?>)) { |
| break; |
| } |
| |
| Collection<Object> newResult = null; |
| if (result instanceof Bag<?>) { |
| newResult = createNewBag(); |
| } else if (result instanceof Set<?>) { |
| newResult = createNewSet(); |
| } else { |
| // Sequence |
| newResult = createNewSequence(); |
| } |
| |
| // the element type is a collection type -- flatten one level |
| newResult.addAll((Collection<?>) object); |
| while (it.hasNext()) { |
| newResult.addAll((Collection<?>) it.next()); |
| } |
| |
| result = newResult; |
| // loop until the result is empty or the first element is not a |
| // collection |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Obtains the type of the flattened form of the specified collection type. |
| * |
| * @param type a collection type |
| * @return the flattened collection type |
| * |
| * @since 1.2 |
| */ |
| @SuppressWarnings("unchecked") |
| public static <C> C getFlattenedElementType( |
| CollectionType<C, ?> type) { |
| |
| C result = type.getElementType(); |
| |
| while (result instanceof CollectionType<?, ?>) { |
| result = ((CollectionType<C, ?>) result).getElementType(); |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Implementation of the OCL |
| * <tt>Set::-(set : Set(T)) : Set(T)</tt> |
| * operation. |
| * |
| * @param self the source set |
| * @param set another set |
| * @return the subtraction of the other set from the source set |
| */ |
| public static <E> Set<E> minus(Set<? extends E> self, Set<? extends E> set) { |
| Set<E> result = createNewSet(self); |
| result.removeAll(set); |
| return result; |
| } |
| |
| /** |
| * Implementation of the OCL |
| * <ul> |
| * <li><tt>Set::excluding(object : T) : Set(T)</tt></li> |
| * <li><tt>Bag::excluding(object : T) : Bag(T)</tt></li> |
| * <li><tt>Sequence::excluding(object : T) : Sequence(T)</tt></li> |
| * </ul> |
| * operations. |
| * |
| * @param self the source collection |
| * @param object an object |
| * @return the source collection without any occurences of the object |
| */ |
| public static <E> Collection<E> excluding(Collection<E> self, Object object) { |
| Collection<E> result = null; |
| if (self instanceof LinkedHashSet<?>) { |
| result = createNewOrderedSet(self); |
| } else if (self instanceof Set<?>) { |
| result = createNewSet(self); |
| } else if (self instanceof Bag<?>) { |
| result = createNewBag(self); |
| } else if (self instanceof List<?>) { |
| List<E> resultSeq = createNewSequence(self); |
| while (resultSeq.remove(object)) { |
| ; // for sequences we need to remove all the matching elements |
| } |
| return resultSeq; |
| } else { |
| throw new RuntimeException("Unsupported collection type "+self.getClass().getName()); //$NON-NLS-1$ |
| } |
| |
| // non-sequences (bags remove all occurrences internally) |
| result.remove(object); |
| return result; |
| } |
| |
| /** |
| * Implementation of the OCL |
| * <tt>Set::symmetricDifference(set : Set(T)) : Set(T)</tt> |
| * operation. |
| * |
| * @param self the source set |
| * @param set another set |
| * @return the set of elements in either the source or the other set but not |
| * in both |
| */ |
| public static <E> Set<E> symmetricDifference(Set<? extends E> self, |
| Set<? extends E> set) { |
| |
| Set<E> result = createNewSet(self); |
| |
| for (E e : set) { |
| if (result.contains(e)) { |
| result.remove(e); |
| } else { |
| result.add(e); |
| } |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Implementation of the OCL |
| * <ul> |
| * <li><tt>Set::including(object : T) : Set(T)</tt></li> |
| * <li><tt>Bag::including(object : T) : Bag(T)</tt></li> |
| * <li><tt>Sequence::including(object : T) : Sequence(T)</tt></li> |
| * </ul> |
| * operations. |
| * |
| * @param self the source collection |
| * @param object an object |
| * @return the source collection with the object added |
| */ |
| public static <E> Collection<E> including(Collection<E> self, E object) { |
| Collection<E> result; |
| |
| if (self instanceof LinkedHashSet<?>) { |
| result = createNewOrderedSet(self); |
| } else if (self instanceof Set<?>) { |
| result = createNewSet(self); |
| } else if (self instanceof Bag<?>) { |
| result = createNewBag(self); |
| } else { |
| result = createNewSequence(self); |
| } |
| |
| result.add(object); |
| |
| return result; |
| } |
| |
| /** |
| * Implementation of the OCL |
| * <ul> |
| * <li><tt>Set::asSet() : Set(T)</tt></li> |
| * <li><tt>Bag::asSet() : Set(T)</tt></li> |
| * <li><tt>Sequence::asSet() : Set(T)</tt></li> |
| * </ul> |
| * operations. |
| * |
| * @param self the source collection |
| * @return the source collection as a set |
| */ |
| public static <E> Set<E> asSet(Collection<E> self) { |
| if (self instanceof Set<?> && !(self instanceof LinkedHashSet<?>)) { |
| return (Set<E>) self; |
| } |
| return createNewSet(self); |
| } |
| |
| /** |
| * Implementation of the OCL |
| * <ul> |
| * <li><tt>Set::asBag() : Bag(T)</tt></li> |
| * <li><tt>Bag::asBag() : Bag(T)</tt></li> |
| * <li><tt>Sequence::asBag() : Bag(T)</tt></li> |
| * </ul> |
| * operations. |
| * |
| * @param self the source collection |
| * @return the source collection as a bag |
| */ |
| public static <E> Bag<E> asBag(Collection<E> self) { |
| if (self instanceof Bag<?>) { |
| return (Bag<E>) self; |
| } |
| return createNewBag(self); |
| } |
| |
| /** |
| * Implementation of the OCL |
| * <ul> |
| * <li><tt>Set::asSequence() : Sequence(T)</tt></li> |
| * <li><tt>Bag::asSequence() : Sequence(T)</tt></li> |
| * <li><tt>Sequence::asSequence() : Sequence(T)</tt></li> |
| * </ul> |
| * operations. |
| * |
| * @param self the source collection |
| * @return the source collection as a sequence |
| */ |
| public static <E> List<E> asSequence(Collection<E> self) { |
| if (self instanceof List<?>) { |
| return (List<E>) self; |
| } |
| return createNewSequence(self); |
| } |
| |
| /** |
| * Implementation of the OCL |
| * <ul> |
| * <li><tt>Set::asOrderedSet() : OrderedSet(T)</tt></li> |
| * <li><tt>Bag::asOrderedSet() : OrderedSet(T)</tt></li> |
| * <li><tt>Sequence::asOrderedSet() : OrderedSet(T)</tt></li> |
| * </ul> |
| * operations. |
| * |
| * @param self the source collection |
| * @return the source collection as an ordered set |
| */ |
| public static <E> LinkedHashSet<E> asOrderedSet(Collection<E> self) { |
| // TODO: create an interface for OrderedSet |
| if (self instanceof LinkedHashSet<?>) { |
| return (LinkedHashSet<E>) self; |
| } |
| return createNewOrderedSet(self); |
| } |
| |
| /** |
| * Implementation of the OCL |
| * <tt>Collection::product(c : Collection(T2)) : Set(Tuple(first : T, second : T2))</tt> |
| * operations. |
| * |
| * @param evalEnv the current evaluation environment (for construction of |
| * tuples) |
| * @param env the current OCL environment (for introspection of the tuple type) |
| * @param self the source collection |
| * @param c another collection |
| * @return the product of the collections |
| */ |
| public static<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> |
| Set<Tuple<O, P>> product(EvaluationEnvironment<C, O, P, CLS, E> evalEnv, |
| Environment<PK, C, O, P, EL, PM, S, COA, SSA, CT, CLS, E> env, |
| Collection<?> self, Collection<?> c, C tupleType) { |
| |
| Set<Tuple<O, P>> result = createNewSet(); |
| |
| Map<P, Object> propertyValues = new HashMap<P, Object>(); |
| P firstProperty = env.lookupProperty( |
| tupleType, |
| OCLStandardLibraryUtil.PRODUCT_FIRST); |
| P secondProperty = env.lookupProperty( |
| tupleType, |
| OCLStandardLibraryUtil.PRODUCT_SECOND); |
| |
| for (Object next1 : self) { |
| for (Object next2 : c) { |
| propertyValues.put(firstProperty, next1); |
| propertyValues.put(secondProperty, next2); |
| |
| result.add(evalEnv.createTuple(tupleType, propertyValues)); |
| } |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Implementation of the OCL |
| * <ul> |
| * <li><tt>OrderedSet::append(object : T) : OrderedSet(T)</tt></li> |
| * <li><tt>Sequence::append(object : T) : Sequence(T)</tt></li> |
| * </ul> |
| * operations. |
| * |
| * @param self the source collection |
| * @param object an object |
| * @return the source collection with the object appended |
| */ |
| public static <E> Collection<E> append(Collection<E> self, E object) { |
| Collection<E> result; |
| // TODO: make an interface for OrderedSet |
| if (self instanceof LinkedHashSet<?>) { |
| result = createNewOrderedSet(self); |
| result.remove(object); // appended object must be last |
| } else { |
| result = createNewSequence(self); |
| } |
| |
| result.add(object); |
| return result; |
| } |
| |
| /** |
| * Implementation of the OCL |
| * <ul> |
| * <li><tt>OrderedSet::prepend(object : T) : OrderedSet(T)</tt></li> |
| * <li><tt>Sequence::prepend(object : T) : Sequence(T)</tt></li> |
| * </ul> |
| * operations. |
| * |
| * @param self the source collection |
| * @param object an object |
| * @return the source collection with the object prepended |
| */ |
| public static <E> Collection<E> prepend(Collection<E> self, E object) { |
| Collection<E> result; |
| if (self instanceof LinkedHashSet<?>) { |
| result = createNewOrderedSet(); |
| } else { |
| result = createNewSequence(); |
| } |
| result.add(object); |
| result.addAll(self); |
| return result; |
| } |
| |
| /** |
| * Implementation of the OCL |
| * <ul> |
| * <li><tt>OrderedSet::insertAt(index : Integer, object : T) : OrderedSet(T)</tt></li> |
| * <li><tt>Sequence::insertAt(index : Integer, object : T) : Sequence(T)</tt></li> |
| * </ul> |
| * operations. |
| * |
| * @param self the source collection |
| * @param index the 1-based (in OCL fashion) index |
| * @param object an object |
| * @return the source collection with the object inserted at the index |
| * |
| * @throws IndexOutOfBoundsException if the index is out of bounds |
| */ |
| public static <E> Collection<E> insertAt(Collection<E> self, int index, E object) { |
| index = index - 1; |
| |
| if (index < 0 || index > self.size()) { |
| throw new IndexOutOfBoundsException( |
| "index: " + (index + 1) + ", size: " //$NON-NLS-1$ //$NON-NLS-2$ |
| + self.size()); |
| } |
| |
| Collection<E> result; |
| if (self instanceof LinkedHashSet<?>) { |
| result = createNewOrderedSet(); |
| } else { |
| result = createNewSequence(); |
| } |
| |
| int curr = 0; |
| for (Iterator<E> it = self.iterator(); it.hasNext();) { |
| if (curr == index) { |
| result.add(object); |
| } |
| result.add(it.next()); |
| curr++; |
| } |
| |
| if (index == self.size()) { |
| // the loop finished before we could add the object |
| result.add(object); |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Implementation of the OCL |
| * <tt>OrderedSet::subOrderedSet(lower : Integer, upper : Integer) : OrderedSet(T)</tt> |
| * operation. |
| * |
| * @param self the source set |
| * @param lower the 1-based (in OCL fashion) inclusive lower bound |
| * @param upper the 1-based (in OCL fashion) inclusive upper bound |
| * @return the slice of the source set |
| * |
| * @throws IndexOutOfBoundsException if an index is out of bounds |
| * @throws IllegalArgumentException if the lower bound is greater than the upper |
| */ |
| public static <E> Collection<E> subOrderedSet(Collection<E> self, int lower, |
| int upper) { |
| lower = lower - 1; |
| upper = upper - 1; |
| |
| if (lower < 0) { |
| throw new IndexOutOfBoundsException("lower: " + (lower + 1)); //$NON-NLS-1$ |
| } else if (upper >= self.size()) { |
| throw new IndexOutOfBoundsException( |
| "upper: " + (upper + 1) + ", size: " //$NON-NLS-1$ //$NON-NLS-2$ |
| + self.size()); |
| } else if (upper < lower) { |
| throw new IllegalArgumentException( |
| "lower: " + (lower + 1) + ", upper: " //$NON-NLS-1$ //$NON-NLS-2$ |
| + (upper + 1)); |
| } |
| |
| Collection<E> result; |
| if (self instanceof LinkedHashSet<?>) { |
| result = createNewOrderedSet(); |
| } else { |
| result = createNewSequence(); |
| } |
| int curr = 0; |
| for (Iterator<E> it = self.iterator(); it.hasNext();) { |
| E object = it.next(); |
| if (curr >= lower && curr <= upper) { |
| result.add(object); |
| } |
| curr++; |
| } |
| return result; |
| } |
| |
| /** |
| * Implementation of the OCL |
| * <tt>Sequence::subSequence(lower : Integer, upper : Integer) : Sequence(T)</tt></li> |
| * operation. |
| * |
| * @param self the source sequence |
| * @param lower the 1-based (in OCL fashion) inclusive lower bound |
| * @param upper the 1-based (in OCL fashion) inclusive upper bound |
| * @return the source collection with the object inserted at the index |
| * |
| * @throws IndexOutOfBoundsException if an index is out of bounds |
| * @throws IllegalArgumentException if the lower bound is greater than the upper |
| */ |
| public static <E> Collection<E> subSequence(Collection<E> self, int lower, |
| int upper) { |
| lower = lower - 1; |
| upper = upper - 1; |
| |
| if (lower < 0) { |
| throw new IndexOutOfBoundsException("lower: " + (lower + 1)); //$NON-NLS-1$ |
| } else if (upper >= self.size()) { |
| throw new IndexOutOfBoundsException( |
| "upper: " + (upper + 1) + ", size: " //$NON-NLS-1$ //$NON-NLS-2$ |
| + self.size()); |
| } else if (upper < lower) { |
| throw new IllegalArgumentException( |
| "lower: " + (lower + 1) + ", upper: " //$NON-NLS-1$ //$NON-NLS-2$ |
| + (upper + 1)); |
| } |
| |
| Collection<E> result = createNewSequence(); |
| int curr = 0; |
| for (Iterator<E> it = self.iterator(); it.hasNext();) { |
| E object = it.next(); |
| if (curr >= lower && curr <= upper) { |
| result.add(object); |
| } |
| curr++; |
| } |
| return result; |
| } |
| |
| /** |
| * Implementation of the OCL |
| * <ul> |
| * <li><tt>OrderedSet::at(index : Integer) : T</tt></li> |
| * <li><tt>Sequence::at(index : Integer) : T</tt></li> |
| * </ul> |
| * operations. |
| * |
| * @param self the source collection |
| * @param index the 1-based (in OCL fashion) index |
| * @return the object at the specified index of the source collection |
| * |
| * @throws IndexOutOfBoundsException if the index is out of bounds |
| */ |
| public static <E> E at(Collection<E> self, int index) { |
| index = index - 1; |
| |
| if (index < 0 || index >= self.size()) { |
| throw new IndexOutOfBoundsException( |
| "index: " + (index + 1) + ", size: " //$NON-NLS-1$ //$NON-NLS-2$ |
| + self.size()); |
| } |
| |
| int curr = 0; |
| for (Iterator<E> it = self.iterator(); it.hasNext();) { |
| E object = it.next(); |
| if (curr++ == index) { |
| return object; |
| } |
| } |
| return null; // undefined |
| } |
| |
| /** |
| * Implementation of the OCL |
| * <ul> |
| * <li><tt>OrderedSet::first() : T</tt></li> |
| * <li><tt>Sequence::first() : T</tt></li> |
| * </ul> |
| * operations. |
| * |
| * @param self the source collection |
| * @return the first object of the source collection |
| */ |
| public static <E> E first(Collection<E> self) { |
| if (self.isEmpty()) { |
| return null; // undefined |
| } |
| return self.iterator().next(); |
| } |
| |
| /** |
| * Implementation of the OCL |
| * <ul> |
| * <li><tt>OrderedSet::lset() : T</tt></li> |
| * <li><tt>Sequence::lset() : T</tt></li> |
| * </ul> |
| * operations. |
| * |
| * @param self the source collection |
| * @return the last object in the source collection |
| */ |
| public static <E> E last(Collection<E> self) { |
| if (self.isEmpty()) { |
| return null; // undefined |
| } |
| E result = null; |
| for (E next : self) { |
| result = next; |
| } |
| return result; |
| } |
| |
| /** |
| * Implementation of the OCL |
| * <ul> |
| * <li><tt>OrderedSet::indexOf(object : T) : Integer</tt></li> |
| * <li><tt>Sequence::indexOf(object : T) : Integer</tt></li> |
| * </ul> |
| * operations. |
| * |
| * @param self the source collection |
| * @param object an object |
| * @return the index of the object in the source collection |
| */ |
| public static <E> Integer indexOf(Collection<? extends E> self, E object) { |
| int index = 1; |
| |
| for (E next : self) { |
| if (ObjectUtil.equal(object, next)) { |
| return index; |
| } |
| index++; |
| } |
| |
| return null; // invalid |
| } |
| |
| /** |
| * Creates a new OCL <tt>Set</tt>. |
| */ |
| @SuppressWarnings("unchecked") |
| public static <E> Set<E> createNewSet() { |
| return (Set<E>) createNewCollection(CollectionKind.SET_LITERAL); |
| } |
| |
| /** |
| * Creates a new OCL <tt>Set</tt> with initial contents supplied. |
| */ |
| @SuppressWarnings("unchecked") |
| public static <E> Set<E> createNewSet(Collection<? extends E> c) { |
| return (Set<E>) createNewCollection(CollectionKind.SET_LITERAL, c); |
| } |
| |
| /** |
| * Creates a new OCL <tt>Bag</tt>. |
| */ |
| @SuppressWarnings("unchecked") |
| public static <E> Bag<E> createNewBag() { |
| return (Bag<E>) createNewCollection(CollectionKind.BAG_LITERAL); |
| } |
| |
| /** |
| * Creates a new OCL <tt>Bag</tt> with initial contents supplied. |
| */ |
| @SuppressWarnings("unchecked") |
| public static <E> Bag<E> createNewBag(Collection<? extends E> c) { |
| return (Bag<E>) createNewCollection(CollectionKind.BAG_LITERAL, c); |
| } |
| |
| /** |
| * Creates a new OCL <tt>OrderedSet</tt>. |
| */ |
| @SuppressWarnings("unchecked") |
| public static <E> LinkedHashSet<E> createNewOrderedSet() { |
| return (LinkedHashSet<E>) createNewCollection( |
| CollectionKind.ORDERED_SET_LITERAL); |
| } |
| |
| /** |
| * Creates a new OCL <tt>OrderedSet</tt> with initial contents supplied. |
| */ |
| @SuppressWarnings("unchecked") |
| public static <E> LinkedHashSet<E> createNewOrderedSet(Collection<? extends E> c) { |
| return (LinkedHashSet<E>) createNewCollection( |
| CollectionKind.ORDERED_SET_LITERAL, c); |
| } |
| |
| /** |
| * Creates a new OCL <tt>Sequence</tt>. |
| */ |
| @SuppressWarnings("unchecked") |
| public static <E> List<E> createNewSequence() { |
| return (List<E>) createNewCollection(CollectionKind.SEQUENCE_LITERAL); |
| } |
| |
| /** |
| * Creates a new OCL <tt>Sequence</tt> with initial contents supplied. |
| */ |
| @SuppressWarnings("unchecked") |
| public static <E> List<E> createNewSequence(Collection<? extends E> c) { |
| return (List<E>) createNewCollection(CollectionKind.SEQUENCE_LITERAL, c); |
| } |
| |
| /** |
| * Creates a new, empty OCL collection of the same kind as the specified |
| * prototype. |
| * |
| * @param c a collection |
| * @return a new, empty collection of the same kind as <code>c</code> |
| */ |
| public static <E> Collection<E> createNewCollectionOfSameKind(Collection<?> c) { |
| Collection<E> result; |
| |
| if (c instanceof Bag<?>) { |
| result = createNewBag(); |
| } else if (c instanceof LinkedHashSet<?>) { |
| result = createNewOrderedSet(); |
| } else if (c instanceof Set<?>) { |
| result = createNewSet(); |
| } else { |
| result = createNewSequence(); |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Creates a new OCL collection of the same kind and contents as the |
| * specified prototype. |
| * |
| * @param c a collection |
| * @return a copy of <code>c</code> |
| */ |
| public static <E> Collection<E> createNewCollection(Collection<? extends E> c) { |
| Collection<E> result; |
| |
| if (c instanceof Bag<?>) { |
| result = createNewBag(c); |
| } else if (c instanceof LinkedHashSet<?>) { |
| result = createNewOrderedSet(c); |
| } else if (c instanceof Set<?>) { |
| result = createNewSet(c); |
| } else { |
| result = createNewSequence(c); |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Creates a new OCL <tt>Collection</tt> of the specified kind. |
| * |
| * @param kind the OCL collection kind |
| * @return the new collection |
| */ |
| public static <E> Collection<E> createNewCollection(CollectionKind kind) { |
| switch (kind) { |
| case SET_LITERAL: |
| return new HashSet<E>(); |
| case SEQUENCE_LITERAL: |
| return new ArrayList<E>(); |
| case ORDERED_SET_LITERAL: |
| return new LinkedHashSet<E>(); |
| case BAG_LITERAL: |
| return new BagImpl<E>(); |
| default: { |
| String message = OCLMessages.bind( |
| OCLMessages.OCLCollectionKindNotImpl_ERROR_, |
| kind); |
| IllegalArgumentException error = new IllegalArgumentException( |
| message); |
| OCLPlugin.throwing( |
| CollectionUtil.class, |
| "createNewCollection", error);//$NON-NLS-1$ |
| throw error; |
| |
| } |
| } |
| } |
| |
| /** |
| * Creates a new OCL <tt>Collection</tt> of the specified kind. |
| * |
| * @param kind the OCL collection kind |
| * @param c the contents of the new collection |
| * @return the new collection of the specified <code>kind</code>, containing |
| * the same elements as <code>c</code> |
| */ |
| public static <E> Collection<E> createNewCollection( |
| CollectionKind kind, Collection<E> c) { |
| switch (kind) { |
| case SET_LITERAL: |
| return new HashSet<E>(c); |
| case SEQUENCE_LITERAL: |
| return new ArrayList<E>(c); |
| case BAG_LITERAL: |
| return new BagImpl<E>(c); |
| case ORDERED_SET_LITERAL: |
| return new LinkedHashSet<E>(c); |
| default: { |
| String message = OCLMessages.bind( |
| OCLMessages.OCLCollectionKindNotImpl_ERROR_, |
| kind); |
| IllegalArgumentException error = new IllegalArgumentException( |
| message); |
| OCLPlugin.throwing( |
| CollectionUtil.class, |
| "createNewCollection", error);//$NON-NLS-1$ |
| throw error; |
| } |
| } |
| } |
| |
| /** |
| * Infers the OCL kind of a collection. |
| * |
| * @param c a collection (not <code>null</code>) |
| * |
| * @return its kind (likewise, not <code>null</code>) |
| */ |
| private static CollectionKind kindOf(Collection<?> c) { |
| CollectionKind result; |
| |
| if (c instanceof List<?>){ |
| result = CollectionKind.SEQUENCE_LITERAL; |
| } else if (c instanceof LinkedHashSet<?>) { |
| result = CollectionKind.ORDERED_SET_LITERAL; |
| } else if (c instanceof Set<?>) { |
| result = CollectionKind.SET_LITERAL; |
| } else if (c instanceof Bag<?>) { |
| result = CollectionKind.BAG_LITERAL; |
| } else { |
| result = CollectionKind.COLLECTION_LITERAL; |
| } |
| |
| return result; |
| } |
| |
| /** |
| * Computes the string representation of a collection value using syntax |
| * like OCL's collection literals (e.g., <tt>OrderedSet{...}</tt>) instead |
| * of Java's default (i.e., <tt>[...]</tt>). |
| * |
| * @param c a collection (not <code>null</code>) |
| * @return the string representation of the specified collection |
| * |
| * @since 1.2 |
| */ |
| public static String toString(Collection<?> c) { |
| StringBuilder result = new StringBuilder(); |
| |
| result.append(kindOf(c).getName()); |
| result.append('{'); |
| |
| boolean notFirst = false; |
| for (Iterator<?> iter = c.iterator();;) { |
| if (iter.hasNext()) { |
| if (notFirst) { |
| result.append(", "); //$NON-NLS-1$ |
| } else { |
| notFirst = true; |
| } |
| |
| Object next = iter.next(); |
| if (next instanceof Collection<?>) { |
| // nested collection |
| result.append(toString((Collection<?>) next)); |
| } else if (next instanceof String) { |
| // string literal |
| result.append('\'').append(next).append('\''); |
| } else { |
| result.append(next); |
| } |
| } else { |
| break; |
| } |
| } |
| |
| result.append('}'); |
| |
| return result.toString(); |
| } |
| } |