001package org.andromda.translation.ocl.validation; 002 003import java.util.ArrayList; 004import java.util.Arrays; 005import java.util.Collection; 006import java.util.Collections; 007import java.util.HashSet; 008import java.util.Iterator; 009import java.util.List; 010import java.util.Random; 011import java.util.Set; 012import java.util.TreeSet; 013import org.apache.commons.collections.Bag; 014import org.apache.commons.collections.CollectionUtils; 015import org.apache.commons.collections.Predicate; 016import org.apache.commons.collections.SetUtils; 017import org.apache.commons.collections.Transformer; 018import org.apache.commons.collections.bag.HashBag; 019import org.apache.commons.lang.StringUtils; 020 021/** 022 * Used to translated OCL collection expressions to their corresponding Java collection expressions. 023 */ 024public final class OCLCollections 025{ 026 /** 027 * Counts the number of occurrences of the argument item in the source collection. 028 * @param collection 029 * @param item 030 * @return CollectionUtils.cardinality(item, collection) 031 */ 032 public static int count( 033 final Collection collection, 034 Object item) 035 { 036 return collection == null ? 0 : CollectionUtils.cardinality(item, collection); 037 } 038 039 /** 040 * Return true if the object is not an element of the collection, false otherwise. 041 * @param collection 042 * @param item 043 * @return !collection.contains(item 044 */ 045 public static boolean excludes( 046 final Collection collection, 047 final Object item) 048 { 049 return collection == null || !collection.contains(item); 050 } 051 052 /** 053 * Returns true if all elements of the parameter collection are not present in the current collection, false 054 * otherwise. 055 * @param collection 056 * @param items 057 * @return excludesAll 058 */ 059 public static boolean excludesAll( 060 final Collection collection, 061 final Collection items) 062 { 063 boolean excludesAll = true; 064 for (final Iterator iterator = items.iterator(); iterator.hasNext();) 065 { 066 final Object object = iterator.next(); 067 if (!excludes(collection, object)) 068 { 069 excludesAll = false; 070 break; 071 } 072 } 073 return excludesAll; 074 } 075 076 /** 077 * Returns true if the object is an element of the collection, false otherwise. 078 * @param collection 079 * @param item 080 * @return collection.contains(item) 081 */ 082 public static boolean includes( 083 final Collection collection, 084 final Object item) 085 { 086 return collection != null && collection.contains(item); 087 } 088 089 /** 090 * Returns true if all elements of the parameter collection are present in the current collection, false otherwise. 091 * @param collection 092 * @param items 093 * @return collection.containsAll(items) 094 */ 095 public static boolean includesAll( 096 final Collection collection, 097 final Collection items) 098 { 099 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}