1 package org.andromda.translation.ocl.validation;
2
3 import java.util.ArrayList;
4 import java.util.Arrays;
5 import java.util.Collection;
6 import java.util.Collections;
7 import java.util.HashSet;
8 import java.util.Iterator;
9 import java.util.List;
10 import java.util.Random;
11 import java.util.Set;
12 import java.util.TreeSet;
13 import org.apache.commons.collections.Bag;
14 import org.apache.commons.collections.CollectionUtils;
15 import org.apache.commons.collections.Predicate;
16 import org.apache.commons.collections.SetUtils;
17 import org.apache.commons.collections.Transformer;
18 import org.apache.commons.collections.bag.HashBag;
19 import org.apache.commons.lang.StringUtils;
20
21 /**
22 * Used to translated OCL collection expressions to their corresponding Java collection expressions.
23 */
24 public final class OCLCollections
25 {
26 /**
27 * Counts the number of occurrences of the argument item in the source collection.
28 * @param collection
29 * @param item
30 * @return CollectionUtils.cardinality(item, collection)
31 */
32 public static int count(
33 final Collection collection,
34 Object item)
35 {
36 return collection == null ? 0 : CollectionUtils.cardinality(item, collection);
37 }
38
39 /**
40 * Return true if the object is not an element of the collection, false otherwise.
41 * @param collection
42 * @param item
43 * @return !collection.contains(item
44 */
45 public static boolean excludes(
46 final Collection collection,
47 final Object item)
48 {
49 return collection == null || !collection.contains(item);
50 }
51
52 /**
53 * Returns true if all elements of the parameter collection are not present in the current collection, false
54 * otherwise.
55 * @param collection
56 * @param items
57 * @return excludesAll
58 */
59 public static boolean excludesAll(
60 final Collection collection,
61 final Collection items)
62 {
63 boolean excludesAll = true;
64 for (final Iterator iterator = items.iterator(); iterator.hasNext();)
65 {
66 final Object object = iterator.next();
67 if (!excludes(collection, object))
68 {
69 excludesAll = false;
70 break;
71 }
72 }
73 return excludesAll;
74 }
75
76 /**
77 * Returns true if the object is an element of the collection, false otherwise.
78 * @param collection
79 * @param item
80 * @return collection.contains(item)
81 */
82 public static boolean includes(
83 final Collection collection,
84 final Object item)
85 {
86 return collection != null && collection.contains(item);
87 }
88
89 /**
90 * Returns true if all elements of the parameter collection are present in the current collection, false otherwise.
91 * @param collection
92 * @param items
93 * @return collection.containsAll(items)
94 */
95 public static boolean includesAll(
96 final Collection collection,
97 final Collection items)
98 {
99 return collection != null && collection.containsAll(items);
100 }
101
102 /**
103 * Returns true if the collection contains no elements, false otherwise.
104 * @param collection
105 * @return collection.isEmpty()
106 */
107 public static boolean isEmpty(final Collection collection)
108 {
109 return (collection == null) || (collection.isEmpty());
110 }
111
112 /**
113 * Returns true if the argument is <code>null</code>, false otherwise.
114 * @param object
115 * @return isEmpty
116 */
117 public static boolean isEmpty(final Object object)
118 {
119 boolean isEmpty = object == null;
120 if (!isEmpty)
121 {
122 if (object instanceof Collection)
123 {
124 isEmpty = ((Collection) object).isEmpty();
125 }
126 else if (object instanceof String)
127 {
128 isEmpty = isEmpty((String) object);
129 }
130 else if (object != null && object.getClass().isArray())
131 {
132 isEmpty = ((Object[]) object).length == 0;
133 }
134 }
135 return isEmpty;
136 }
137
138 /**
139 * Returns true if the argument is either <code>null</code> or only contains whitespace characters, false
140 * otherwise.
141 * @param string
142 * @return isBlank(string)
143 */
144 public static boolean isEmpty(final String string)
145 {
146 return StringUtils.isBlank(string);
147 }
148
149 /**
150 * Returns true if the collection contains one or more elements, false otherwise.
151 * @param collection
152 * @return isEmpty(collection)
153 */
154 public static boolean notEmpty(final Collection collection)
155 {
156 return (collection != null) && !isEmpty(collection);
157 }
158
159 /**
160 * Returns true if the argument is not <code>null</code>, false otherwise.
161 * @param object
162 * @return notEmpty
163 */
164 public static boolean notEmpty(final Object object)
165 {
166 boolean notEmpty = object != null;
167 if (notEmpty)
168 {
169 if (object instanceof Collection)
170 {
171 notEmpty = !((Collection) object).isEmpty();
172 }
173 else if (object instanceof String)
174 {
175 notEmpty = notEmpty((String) object);
176 }
177 else if (object != null && object.getClass().isArray())
178 {
179 notEmpty = ((Object[]) object).length > 0;
180 }
181 }
182 return notEmpty;
183 }
184
185 /**
186 * Returns true if the argument is neither <code>null</code> nor only contains whitespace characters, false
187 * otherwise.
188 * @param string
189 * @return isNotBlank(string)
190 */
191 public static boolean notEmpty(final String string)
192 {
193 return StringUtils.isNotBlank(string);
194 }
195
196 /**
197 * Checks the instance of the <code>object</code> and makes sure its a Collection, if the object is a collection the
198 * size is checked and returned, if its NOT a collection, 0 is returned.
199 *
200 * @param object the object to check.
201 * @return the size of the collection
202 */
203 public static int size(final Object object)
204 {
205 int size = 0;
206 if (object != null)
207 {
208 if (object instanceof Collection)
209 {
210 size = size((Collection) object);
211 } else if (object.getClass().isArray())
212 {
213 size = ((Object[]) object).length;
214 }
215 }
216 return size;
217 }
218
219 /**
220 * Returns the number of elements in the collection.
221 * @param collection
222 * @return collection.size()
223 */
224 public static int size(final Collection collection)
225 {
226 int size = 0;
227 if (collection != null)
228 {
229 size = collection.size();
230 }
231 return size;
232 }
233
234 /**
235 * Returns the sum of all the elements in the collection. Every element must extend Number or this method
236 * will throw an exception.
237 *
238 * @param collection a collection containing only classes extending Number
239 * @return the sum of all the elements in the collection
240 */
241 public static double sum(final Object collection)
242 {
243 double sum = 0;
244 if (collection != null)
245 {
246 if (collection instanceof Collection)
247 {
248 // TODO Fix infinite loop
249 sum = sum(collection);
250 }
251 else if (collection.getClass().isArray())
252 {
253 sum = sum(Arrays.asList((Object[]) collection));
254 }
255 }
256 return sum;
257 }
258
259 /**
260 * Returns the sum of all the element in the collection. Every element must extend Number or this method
261 * will throw an exception.
262 *
263 * @param collection a collection containing only classes extending Number
264 * @return the sum of all the elements in the collection
265 */
266 public static double sum(final Collection collection)
267 {
268 double sum = 0;
269 if (collection != null && !collection.isEmpty())
270 {
271 for (final Iterator iterator = collection.iterator(); iterator.hasNext();)
272 {
273 Object object = iterator.next();
274 if (object instanceof Number)
275 {
276 sum += ((Number) object).doubleValue();
277 } else
278 {
279 throw new UnsupportedOperationException(
280 "In order to calculate the sum of a collection\'s elements " +
281 "all of them must extend Number, found: " + object.getClass().getName());
282 }
283 }
284 }
285 return sum;
286 }
287
288 /**
289 * Appends the item to the list.
290 * @param list
291 * @param item
292 * @return true if the operation was a success
293 */
294 public static boolean append(
295 final List list,
296 final Object item)
297 {
298 return list == null ? false : list.add(item);
299 }
300
301 /**
302 * Insert the item into the first position of the list.
303 * @param list
304 * @param item
305 * @return the element previously at the first position
306 */
307 public static Object prepend(
308 final List list,
309 final Object item)
310 {
311 return list.set(0, item);
312 }
313
314 /**
315 * Appends the item to the bag.
316 * @param collection
317 * @param item
318 * @return true if the operation was a success
319 */
320 public static boolean append(
321 final Bag collection,
322 final Object item)
323 {
324 return collection == null ? false : collection.add(item);
325 }
326
327 /**
328 * Returns the argument as a bag.
329 * @param collection
330 * @return new HashBag(collection)
331 */
332 public static Bag asBag(final Collection collection)
333 {
334 return collection == null ? new HashBag() : new HashBag(collection);
335 }
336
337 /**
338 * Returns the argument as an ordered set.
339 * @param collection
340 * @return SetUtils.orderedSet(new TreeSet(collection))
341 */
342 public static Set asOrderedSet(final Collection collection)
343 {
344 return collection == null ? Collections.emptySet() : SetUtils.orderedSet(new TreeSet(collection));
345 }
346
347 /**
348 * Returns the argument as a list.
349 * @param collection
350 * @return new ArrayList(collection)
351 */
352 public static List asSequence(final Collection collection)
353 {
354 return collection == null ? Collections.emptyList() : new ArrayList(collection);
355 }
356
357 /**
358 * Returns the argument as a set.
359 * @param collection
360 * @return new HashSet(collection)
361 */
362 public static Set asSet(final Collection collection)
363 {
364 return collection == null ? Collections.emptySet() : new HashSet(collection);
365 }
366
367 /**
368 * Returns the element at the specified index in the argument list.
369 * @param list
370 * @param index
371 * @return list.get(index)
372 */
373 public static Object at(
374 final List list,
375 final int index)
376 {
377 return list == null ? null : list.get(index);
378 }
379
380 /**
381 * Removes all occurrences of the item in the source collection.
382 * @param collection
383 * @param item
384 * @return true if one or more elements were removed
385 */
386 public static boolean excluding(
387 final Collection collection,
388 final Object item)
389 {
390 return collection == null ? false : collection.remove(item);
391 }
392
393 /**
394 * Adds the item to the list
395 * @param collection
396 * @param item
397 * @return true if the element was added
398 */
399 public static boolean including(
400 final Collection collection,
401 final Object item)
402 {
403 return collection == null ? false : collection.add(item);
404 }
405
406 /**
407 * Recursively flattens this collection, this method returns a Collection containing no nested Collection
408 * instances.
409 * @param collection
410 * @return flattenedCollection.addAll(flatten((Collection) object))
411 */
412 public static Collection flatten(final Collection collection)
413 {
414 final Collection flattenedCollection = new ArrayList();
415 for (final Iterator iterator = collection.iterator(); iterator.hasNext();)
416 {
417 final Object object = iterator.next();
418 if (object instanceof Collection)
419 {
420 flattenedCollection.addAll(flatten((Collection) object));
421 }
422 else
423 {
424 flattenedCollection.add(object);
425 }
426 }
427
428 return flattenedCollection;
429 }
430
431 /**
432 * Returns the index in this list of the first occurrence of the specified element, or -1 if this list does not
433 * contain this element. More formally, returns the lowest index i such that (o == null ? get(i) = =null :
434 * o.equals(get(i))), or -1 if there is no such index.
435 * @param collection
436 * @param item
437 * @return collection.indexOf(item)
438 */
439 public static int indexOf(
440 final List collection,
441 final Object item)
442 {
443 return collection == null ? -1 : collection.indexOf(item);
444 }
445
446 /**
447 * Insert the item at the specified index into the collection.
448 * @param collection
449 * @param index
450 * @param item
451 */
452 public static void insertAt(
453 final List collection,
454 int index,
455 Object item)
456 {
457 collection.add(index, item);
458 }
459
460 /**
461 * Returns the collection of elements common in both argument collections.
462 * @param first
463 * @param second
464 * @return CollectionUtils.intersection(first, second)
465 */
466 public static Collection intersection(
467 final Collection first,
468 final Collection second)
469 {
470 return CollectionUtils.intersection(first, second);
471 }
472
473 /**
474 * Returns the union of both collections into a single collection.
475 * @param first
476 * @param second
477 * @return CollectionUtils.union(first, second)
478 */
479 public static Collection union(
480 final Collection first,
481 final Collection second)
482 {
483 return CollectionUtils.union(first, second);
484 }
485
486 /**
487 * Returns the last element in the collection.
488 *
489 * @param object the collection or single instance which will be converted to a collection.
490 * @return the last object of the collection or the object itself if the object is not a collection instance (or
491 * null if the object is null or an empty collection).
492 */
493 public static Object last(final Object object)
494 {
495 Object last = null;
496 final List list = objectToList(object);
497 if (!list.isEmpty())
498 {
499 last = list.get(list.size() - 1);
500 }
501 return last;
502 }
503
504 /**
505 * Returns the first element in the collection.
506 *
507 * @param object the collection or single instance which will be converted to a collection.
508 * @return the first object of the collection or the object itself if the object is not a collection instance (or
509 * null if the object is null or an empty collection).
510 */
511 public static Object first(final Object object)
512 {
513 Object first = null;
514 final List list = objectToList(object);
515 if (!list.isEmpty())
516 {
517 first = list.get(0);
518 }
519 return first;
520 }
521
522 /**
523 * Returns those element that are contained in only one of both collections.
524 * @param first
525 * @param second
526 * @return CollectionUtils.disjunction(first, second)
527 */
528 public static Collection symmetricDifference(
529 final Collection first,
530 final Collection second)
531 {
532 return CollectionUtils.disjunction(first, second);
533 }
534
535 /**
536 * TODO: implement
537 * @param collection
538 * @return UnsupportedOperationException
539 */
540 public static Set subOrderedSet(final Set collection)
541 {
542 throw new UnsupportedOperationException(OCLCollections.class.getName() + ".subOrderedSet");
543 }
544
545 /**
546 * TODO: implement
547 * @param collection
548 * @return UnsupportedOperationException
549 */
550 public static List subSequence(final List collection)
551 {
552 throw new UnsupportedOperationException(OCLCollections.class.getName() + ".subSequence");
553 }
554
555 /**
556 * Returns a random element from the collection for which the argument expression evaluates true.
557 * @param collection
558 * @param predicate
559 * @return selectedElements.get(random.nextInt(selectedElements.size())
560 */
561 public static Object any(
562 final Collection collection,
563 final Predicate predicate)
564 {
565 final List selectedElements = new ArrayList(select(collection, predicate));
566 final Random random = new Random(System.currentTimeMillis());
567 return selectedElements.isEmpty() ? null : selectedElements.get(random.nextInt(selectedElements.size()));
568 }
569
570 /**
571 * Returns the collection of Objects that results from executing the transformer on each individual element in the
572 * source collection.
573 * @param collection
574 * @param transformer
575 * @return CollectionUtils.collect(collection, transformer)
576 */
577 public static Collection collect(
578 final Collection collection,
579 final Transformer transformer)
580 {
581 return CollectionUtils.collect(collection, transformer);
582 }
583
584 /**
585 * TODO: implement
586 * @param collection
587 * @return UnsupportedOperationException
588 */
589 public static Collection collectNested(final Collection collection)
590 {
591 throw new UnsupportedOperationException(OCLCollections.class.getName() + ".collectNested");
592 }
593
594 /**
595 * Returns true if a predicate is true for at least one element of a collection. <p/>A null collection or predicate
596 * returns false.
597 * @param collection
598 * @param predicate
599 * @return CollectionUtils.exists(collection, predicate)
600 */
601 public static boolean exists(
602 final Collection collection,
603 final Predicate predicate)
604 {
605 return CollectionUtils.exists(collection, predicate);
606 }
607
608 /**
609 * Returns true if a predicate is true for at least one element of a collection. <p/>A null collection or predicate
610 * returns false.
611 * @param collection
612 * @param predicate
613 * @return exists((Collection) collection, predicate)
614 */
615 public static boolean exists(
616 final Object collection,
617 final Predicate predicate)
618 {
619 return collection instanceof Collection ? exists((Collection) collection, predicate) : false;
620 }
621
622 /**
623 * <p/>
624 * Executes every <code>predicate</code> for the given collectoin, if one evaluates to <code>false</code> this
625 * operation returns <code>false</code>, otherwise <code>true</code> is returned. </p> If the input collection or
626 * closure is null <code>false</code> is returned.
627 * @param collection
628 * @param predicate
629 * @return true if every evaluated predicate returns true, false otherwise.
630 */
631 public static boolean forAll(
632 final Collection collection,
633 final Predicate predicate)
634 {
635 boolean valid = collection != null;
636 if (valid && collection != null)
637 {
638 for (final Iterator iterator = collection.iterator(); iterator.hasNext();)
639 {
640 final Object object = iterator.next();
641 valid = predicate.evaluate(object);
642 if (!valid)
643 {
644 break;
645 }
646 }
647 }
648 return valid;
649 }
650
651 /**
652 * <p/>
653 * Executes every <code>predicate</code> for the given collection, if one evaluates to <code>false</code> this
654 * operation returns <code>false</code>, otherwise <code>true</code> is returned. </p> If the input collection or
655 * closure is null <code>false</code> is returned.
656 * @param collection
657 * @param predicate
658 *
659 * @return true if every evaluated predicate returns true, false otherwise.
660 */
661 public static boolean forAll(
662 final Object collection,
663 final Predicate predicate)
664 {
665 boolean valid = false;
666 if (collection instanceof Collection)
667 {
668 valid = forAll((Collection) collection, predicate);
669 }
670 return valid;
671 }
672
673 /**
674 * Returns <code>true</code> if the result of executing the <code>transformer</code> has a unique value for each
675 * element in the source collection.
676 * @param collection
677 * @param transformer
678 * @return unique
679 */
680 public static boolean isUnique(
681 final Collection collection,
682 final Transformer transformer)
683 {
684 boolean unique = true;
685 final Set collected = new HashSet();
686 for (final Iterator iterator = collection.iterator(); iterator.hasNext() && unique;)
687 {
688 final Object result = transformer.transform(iterator.next());
689 if (collected.contains(result))
690 {
691 unique = false;
692 }
693 else
694 {
695 collected.add(result);
696 }
697 }
698 return unique;
699 }
700
701 /**
702 * Returns <code>true</code> if the result of executing the <code>transformer</code> has a unique value for each
703 * element in the source collection.
704 * @param collection
705 * @param transformer
706 * @return isUnique
707 */
708 public static boolean isUnique(
709 final Object collection,
710 final Transformer transformer)
711 {
712 boolean unique = collection != null;
713 if (unique && collection != null && Collection.class.isAssignableFrom(collection.getClass()))
714 {
715 unique = isUnique((Collection) collection, transformer);
716 }
717 return unique;
718 }
719
720 /**
721 * TODO: implement
722 * @param collection
723 * @return UnsupportedOperationException
724 */
725 public static Collection iterate(final Collection collection)
726 {
727 throw new UnsupportedOperationException(OCLCollections.class.getName() + ".iterate");
728 }
729
730 /**
731 * Returns <code>true</true> when the argument expression evaluates true for one and only one element in the
732 * collection. Returns <code>false</code> otherwise.
733 * @param collection
734 * @param predicate
735 * @return found
736 */
737 public static boolean one(
738 final Collection collection,
739 final Predicate predicate)
740 {
741 boolean found = false;
742
743 if (collection != null)
744 {
745 for (final Iterator iterator = collection.iterator(); iterator.hasNext();)
746 {
747 if (predicate.evaluate(iterator.next()))
748 {
749 if (found)
750 {
751 found = false;
752 break;
753 }
754 found = true;
755 }
756 }
757 }
758 return found;
759 }
760
761 /**
762 * <p/>
763 * Returns <code>true</true> if <code>collection</code> is actually a Collection instance and if the
764 * <code>predicate</code> expression evaluates true for one and only one element in the collection. Returns
765 * <code>false</code> otherwise. </p>
766 * @param collection
767 * @param predicate
768 * @return one((Collection) collection, predicate)
769 */
770 public static boolean one(
771 final Object collection,
772 final Predicate predicate)
773 {
774 return collection != null && Collection.class.isAssignableFrom(collection.getClass()) &&
775 one((Collection) collection, predicate);
776 }
777
778 /**
779 * Returns a subcollection of the source collection containing all elements for which the expression evaluates
780 * <code>false</code>.
781 * @param collection
782 * @param predicate
783 * @return CollectionUtils.selectRejected(collection, predicate)
784 */
785 public static Collection reject(
786 final Collection collection,
787 final Predicate predicate)
788 {
789 return CollectionUtils.selectRejected(collection, predicate);
790 }
791
792 /**
793 * Returns a subcollection of the source collection containing all elements for which the expression evaluates
794 * <code>true</code>.
795 * @param collection
796 * @param predicate
797 * @return CollectionUtils.select(collection, predicate)
798 */
799 public static Collection select(
800 final Collection collection,
801 final Predicate predicate)
802 {
803 return CollectionUtils.select(collection, predicate);
804 }
805
806 /**
807 * Returns a subcollection of the source collection containing all elements for which the expression evaluates
808 * <code>true</code>.
809 * @param collection
810 * @param predicate
811 * @return CollectionUtils.select((Collection) collection, predicate)
812 */
813 public static Collection select(
814 final Object collection,
815 final Predicate predicate)
816 {
817 return CollectionUtils.select((Collection) collection, predicate);
818 }
819
820 /**
821 * TODO: implement
822 * @param collection
823 * @return UnsupportedOperationException
824 */
825 public static Collection sortedBy(final Collection collection)
826 {
827 throw new UnsupportedOperationException(OCLCollections.class.getName() + ".sortedBy");
828 }
829
830 /**
831 * Converts the given object to a java.util.List implementation. If the object is not a collection type, then the
832 * object is placed within a collection as the only element.
833 *
834 * @param object the object to convert.
835 * @return the new List.
836 */
837 private static List objectToList(Object object)
838 {
839 List list = null;
840 if (object instanceof Collection)
841 {
842 final Collection collection = (Collection) object;
843 if (!(object instanceof List))
844 {
845 object = new ArrayList(collection);
846 }
847 list = (List) object;
848 } else
849 {
850 list = new ArrayList();
851 if (object != null)
852 {
853 list.add(object);
854 }
855 }
856 return list;
857 }
858 }