blob: 24374b88dd88324916546efa28442c30710e953a [file] [log] [blame]
/*******************************************************************************
* 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();
}
}