Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorkmoore2011-02-05 11:22:02 -0500
committerkmoore2011-02-05 11:22:02 -0500
commit824998d08071af6d9877fb5314aa25d360d1c172 (patch)
tree80d029dbe3dc505c45ea0413e5218fda4b7be5e1 /common/plugins
parentc73d0c7b4a6a3150dd604d6c747069408ace5719 (diff)
downloadwebtools.dali-824998d08071af6d9877fb5314aa25d360d1c172.tar.gz
webtools.dali-824998d08071af6d9877fb5314aa25d360d1c172.tar.xz
webtools.dali-824998d08071af6d9877fb5314aa25d360d1c172.zip
rename org.eclipse.jpt.utility to org.eclipse.jpt.common.utility and move to common component
Diffstat (limited to 'common/plugins')
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/.classpath7
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/.cvsignore4
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/.project28
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/.settings/org.eclipse.core.resources.prefs3
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/.settings/org.eclipse.jdt.core.prefs7
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/META-INF/MANIFEST.MF134
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/about.html34
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/build.properties17
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/component.xml11
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/plugin.properties24
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/Command.java87
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/CommandExecutor.java61
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/Filter.java125
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/IndentingPrintWriter.java155
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/JavaType.java135
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/MethodSignature.java73
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/ObjectReference.java29
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/ReadOnlyObjectReference.java43
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/AbstractAssociation.java69
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/ArrayTools.java3122
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/Association.java46
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/AsynchronousCommandExecutor.java168
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/Bag.java197
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/BidiFilter.java122
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/BidiStringConverter.java149
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/BidiTransformer.java93
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/BitTools.java214
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/BooleanReference.java48
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/BooleanTools.java105
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/ClassName.java431
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/Classpath.java939
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/CollectionTools.java1957
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/CommandRunnable.java37
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/CompositeCommand.java44
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/CompositeException.java96
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/ConsumerThreadCoordinator.java253
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/ExceptionHandler.java87
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/FileTools.java1002
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/FlaggedObjectReference.java69
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/HashBag.java877
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/IdentityHashBag.java924
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/IntReference.java40
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/JDBCTools.java349
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/JDBCType.java162
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/KeyedSet.java129
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/LazyReadOnlyObjectReference.java107
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/ListenerList.java171
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/NameTools.java376
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/NonNullBooleanTransformer.java79
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/NotBooleanTransformer.java56
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/NotNullFilter.java51
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/NullList.java153
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/Queue.java75
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/Range.java87
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/ReadOnlyBooleanReference.java46
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/ReadOnlyIntReference.java145
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/ReflectionTools.java1544
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/ReverseComparator.java40
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/RunnableCommand.java37
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/SimpleAssociation.java69
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/SimpleBooleanReference.java107
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/SimpleCommandExecutor.java46
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/SimpleFilter.java107
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/SimpleIntReference.java186
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/SimpleJavaType.java213
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/SimpleMethodSignature.java240
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/SimpleObjectReference.java98
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/SimpleQueue.java90
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/SimpleStack.java100
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/SimpleStringMatcher.java259
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/SimpleThreadFactory.java53
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/Stack.java75
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/StatefulCommandExecutor.java33
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/StringConverter.java80
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/StringMatcher.java65
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/StringTools.java4708
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/SynchronizedBag.java220
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/SynchronizedBoolean.java437
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/SynchronizedInt.java914
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/SynchronizedObject.java472
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/SynchronizedQueue.java348
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/SynchronizedStack.java325
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/ThreadLocalCommand.java63
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/ThreadLocalCommandExecutor.java65
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/Tools.java89
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/Transformer.java95
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/XMLStringEncoder.java182
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/enumerations/EmptyEnumeration.java62
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/enumerations/IteratorEnumeration.java57
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterables/ArrayIterable.java77
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterables/ArrayListIterable.java59
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterables/ChainIterable.java96
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterables/CloneIterable.java66
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterables/CloneListIterable.java92
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterables/CompositeIterable.java98
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterables/CompositeListIterable.java135
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterables/EmptyIterable.java65
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterables/EmptyListIterable.java65
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterables/FilteringIterable.java95
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterables/GraphIterable.java156
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterables/ListIterable.java27
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterables/ListListIterable.java35
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterables/LiveCloneIterable.java85
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterables/LiveCloneListIterable.java85
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterables/PeekableIterable.java56
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterables/QueueIterable.java51
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterables/ReadOnlyCompositeListIterable.java100
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterables/ReadOnlyIterable.java50
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterables/ReadOnlyListIterable.java50
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterables/SingleElementIterable.java55
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterables/SingleElementListIterable.java58
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterables/SnapshotCloneIterable.java124
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterables/SnapshotCloneListIterable.java102
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterables/StackIterable.java51
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterables/SubIterableWrapper.java47
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterables/SubListIterableWrapper.java52
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterables/SuperIterableWrapper.java48
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterables/SuperListIterableWrapper.java54
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterables/TransformationIterable.java91
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterables/TransformationListIterable.java111
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterables/TreeIterable.java137
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterators/ArrayIterator.java88
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterators/ArrayListIterator.java93
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterators/ChainIterator.java159
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterators/CloneIterator.java191
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterators/CloneListIterator.java291
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterators/CompositeIterator.java162
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterators/CompositeListIterator.java270
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterators/EmptyIterator.java69
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterators/EmptyListIterator.java93
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterators/EnumerationIterator.java52
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterators/FilteringIterator.java148
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterators/GraphIterator.java283
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterators/PeekableIterator.java112
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterators/QueueIterator.java59
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterators/ReadOnlyCompositeListIterator.java252
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterators/ReadOnlyIterator.java65
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterators/ReadOnlyListIterator.java108
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterators/ResultSetIterator.java162
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterators/ReverseIterator.java82
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterators/SingleElementIterator.java67
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterators/SingleElementListIterator.java98
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterators/StackIterator.java59
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterators/SubIteratorWrapper.java59
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterators/SubListIteratorWrapper.java90
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterators/SuperIteratorWrapper.java58
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterators/SuperListIteratorWrapper.java88
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterators/SynchronizedIterator.java76
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterators/SynchronizedListIterator.java122
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterators/TransformationIterator.java103
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterators/TransformationListIterator.java152
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/iterators/TreeIterator.java254
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/AbstractModel.java1007
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/AspectChangeSupport.java349
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/ChangeSupport.java2844
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/SingleAspectChangeSupport.java380
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/listener/awt/AWTChangeListenerWrapper.java454
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/listener/awt/AWTCollectionChangeListenerWrapper.java161
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/listener/awt/AWTListChangeListenerWrapper.java211
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/listener/awt/AWTPropertyChangeListenerWrapper.java87
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/listener/awt/AWTStateChangeListenerWrapper.java86
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/listener/awt/AWTTreeChangeListenerWrapper.java161
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/AbstractCollectionValueModel.java124
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/AbstractListValueModel.java124
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/AbstractPropertyValueModel.java124
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/AbstractPropertyValueModelAdapter.java117
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/AbstractTreeNodeValueModel.java194
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/AspectAdapter.java266
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/AspectCollectionValueModelAdapter.java155
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/AspectListValueModelAdapter.java197
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/AspectPropertyValueModelAdapter.java178
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/AspectTreeValueModelAdapter.java119
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/BufferedWritablePropertyValueModel.java392
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/CachingTransformationPropertyValueModel.java112
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/CachingTransformationWritablePropertyValueModel.java107
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/ChangePropertyValueModelAdapter.java99
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/CollectionAspectAdapter.java158
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/CollectionListValueModelAdapter.java217
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/CollectionPropertyValueModelAdapter.java139
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/CollectionValueModelWrapper.java132
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/CompositeBooleanPropertyValueModel.java338
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/CompositeCollectionValueModel.java448
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/CompositeListValueModel.java683
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/CompositePropertyValueModel.java198
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/ExtendedListValueModelWrapper.java211
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/FilteringCollectionValueModel.java179
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/FilteringPropertyValueModel.java142
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/FilteringWritablePropertyValueModel.java123
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/ItemAspectListValueModelAdapter.java274
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/ItemChangeListValueModelAdapter.java68
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/ItemCollectionListValueModelAdapter.java101
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/ItemListListValueModelAdapter.java109
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/ItemPropertyListValueModelAdapter.java84
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/ItemStateListValueModelAdapter.java74
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/ItemTreeListValueModelAdapter.java101
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/ListAspectAdapter.java176
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/ListCollectionValueModelAdapter.java233
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/ListCurator.java226
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/ListPropertyValueModelAdapter.java167
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/ListValueModelWrapper.java164
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/NullCollectionValueModel.java58
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/NullListValueModel.java71
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/NullPropertyValueModel.java49
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/NullTreeValueModel.java52
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/PropertyAspectAdapter.java128
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/PropertyCollectionValueModelAdapter.java141
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/PropertyListValueModelAdapter.java157
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/PropertyValueModelWrapper.java92
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/ReadOnlyWritablePropertyValueModelWrapper.java49
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/SetCollectionValueModel.java134
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/SimpleCollectionValueModel.java188
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/SimpleListValueModel.java322
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/SimplePropertyValueModel.java66
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/SortedListValueModelAdapter.java125
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/SortedListValueModelWrapper.java250
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/StatePropertyValueModelAdapter.java96
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/StaticCollectionValueModel.java73
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/StaticListValueModel.java93
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/StaticPropertyValueModel.java53
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/StaticTreeValueModel.java57
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/TransformationListValueModel.java309
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/TransformationPropertyValueModel.java144
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/TransformationWritablePropertyValueModel.java131
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/TreeAspectAdapter.java155
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/TreePropertyValueModelAdapter.java144
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/ValueAspectAdapter.java201
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/ValueChangeAdapter.java75
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/ValueCollectionAdapter.java107
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/ValueListAdapter.java123
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/ValuePropertyAdapter.java82
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/ValueStateAdapter.java73
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/ValueTreeAdapter.java107
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/WritablePropertyCollectionValueModelAdapter.java62
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/WritablePropertyListValueModelAdapter.java62
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/prefs/PreferencePropertyValueModel.java346
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/prefs/PreferencesCollectionValueModel.java203
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/swing/AbstractTreeModel.java216
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/swing/CheckBoxModelAdapter.java43
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/swing/ColumnAdapter.java49
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/swing/ComboBoxModelAdapter.java140
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/swing/DateSpinnerModelAdapter.java198
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/swing/DocumentAdapter.java375
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/swing/ListModelAdapter.java292
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/swing/ListSpinnerModelAdapter.java218
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/swing/NumberSpinnerModelAdapter.java223
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/swing/ObjectListSelectionModel.java427
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/swing/PrimitiveListTreeModel.java239
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/swing/RadioButtonModelAdapter.java151
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/swing/SpinnerModelAdapter.java207
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/swing/TableModelAdapter.java420
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/swing/ToggleButtonModelAdapter.java224
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/model/value/swing/TreeModelAdapter.java914
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/node/AbstractNode.java941
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/node/AsynchronousValidator.java50
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/node/DefaultProblem.java85
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/node/Node.java377
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/node/PluggableValidator.java121
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/node/Problem.java51
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/node/SynchronousValidator.java44
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/swing/CachingComboBoxModel.java42
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/swing/CheckBoxTableCellRenderer.java206
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/swing/ComboBoxTableCellRenderer.java328
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/swing/Displayable.java44
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/swing/EmptyIcon.java54
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/swing/FilteringListBrowser.java140
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/swing/FilteringListPanel.java455
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/swing/ListChooser.java430
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/swing/NodeSelector.java32
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/swing/NonCachingComboBoxModel.java73
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/swing/SimpleDisplayable.java170
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/swing/SimpleListBrowser.java86
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/swing/SimpleListCellRenderer.java128
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/swing/SpinnerTableCellRenderer.java186
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/swing/TableCellEditorAdapter.java96
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/synchronizers/AsynchronousSynchronizer.java188
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/synchronizers/CallbackAsynchronousSynchronizer.java120
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/synchronizers/CallbackSynchronousSynchronizer.java83
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/synchronizers/SynchronousSynchronizer.java263
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/model/Model.java143
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/model/event/ChangeEvent.java66
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/model/event/CollectionAddEvent.java124
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/model/event/CollectionChangeEvent.java105
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/model/event/CollectionClearEvent.java61
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/model/event/CollectionEvent.java63
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/model/event/CollectionRemoveEvent.java112
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/model/event/ListAddEvent.java134
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/model/event/ListChangeEvent.java105
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/model/event/ListClearEvent.java61
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/model/event/ListEvent.java64
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/model/event/ListMoveEvent.java120
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/model/event/ListRemoveEvent.java134
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/model/event/ListReplaceEvent.java150
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/model/event/PropertyChangeEvent.java109
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/model/event/StateChangeEvent.java51
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/model/event/TreeAddEvent.java81
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/model/event/TreeChangeEvent.java90
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/model/event/TreeClearEvent.java61
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/model/event/TreeEvent.java62
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/model/event/TreeRemoveEvent.java81
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/model/listener/ChangeAdapter.java109
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/model/listener/ChangeListener.java25
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/model/listener/CollectionChangeAdapter.java51
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/model/listener/CollectionChangeListener.java65
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/model/listener/CommandChangeListener.java44
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/model/listener/ListChangeAdapter.java61
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/model/listener/ListChangeListener.java87
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/model/listener/MultiMethodReflectiveChangeListener.java160
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/model/listener/PropertyChangeAdapter.java31
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/model/listener/PropertyChangeListener.java37
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/model/listener/ReflectiveChangeListener.java377
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/model/listener/SimpleChangeListener.java131
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/model/listener/SingleMethodReflectiveChangeListener.java60
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/model/listener/StateChangeAdapter.java31
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/model/listener/StateChangeListener.java37
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/model/listener/TreeChangeAdapter.java51
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/model/listener/TreeChangeListener.java67
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/model/value/CollectionValueModel.java42
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/model/value/ListValueModel.java57
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/model/value/PropertyValueModel.java36
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/model/value/TreeNodeValueModel.java74
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/model/value/TreeValueModel.java36
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/model/value/WritableCollectionValueModel.java34
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/model/value/WritableListValueModel.java34
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/model/value/WritablePropertyValueModel.java33
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/synchronizers/CallbackSynchronizer.java92
-rw-r--r--common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/synchronizers/Synchronizer.java83
326 files changed, 62391 insertions, 0 deletions
diff --git a/common/plugins/org.eclipse.jpt.common.utility/.classpath b/common/plugins/org.eclipse.jpt.common.utility/.classpath
new file mode 100644
index 0000000000..304e86186a
--- /dev/null
+++ b/common/plugins/org.eclipse.jpt.common.utility/.classpath
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path="src"/>
+ <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/>
+ <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
+ <classpathentry kind="output" path="bin"/>
+</classpath>
diff --git a/common/plugins/org.eclipse.jpt.common.utility/.cvsignore b/common/plugins/org.eclipse.jpt.common.utility/.cvsignore
new file mode 100644
index 0000000000..a128605b1f
--- /dev/null
+++ b/common/plugins/org.eclipse.jpt.common.utility/.cvsignore
@@ -0,0 +1,4 @@
+bin
+@dot
+temp.folder
+build.xml \ No newline at end of file
diff --git a/common/plugins/org.eclipse.jpt.common.utility/.project b/common/plugins/org.eclipse.jpt.common.utility/.project
new file mode 100644
index 0000000000..dbe6e576b1
--- /dev/null
+++ b/common/plugins/org.eclipse.jpt.common.utility/.project
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>org.eclipse.jpt.common.utility</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.jdt.core.javabuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.ManifestBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ <buildCommand>
+ <name>org.eclipse.pde.SchemaBuilder</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.pde.PluginNature</nature>
+ <nature>org.eclipse.jdt.core.javanature</nature>
+ </natures>
+</projectDescription>
diff --git a/common/plugins/org.eclipse.jpt.common.utility/.settings/org.eclipse.core.resources.prefs b/common/plugins/org.eclipse.jpt.common.utility/.settings/org.eclipse.core.resources.prefs
new file mode 100644
index 0000000000..8e5b2c2b65
--- /dev/null
+++ b/common/plugins/org.eclipse.jpt.common.utility/.settings/org.eclipse.core.resources.prefs
@@ -0,0 +1,3 @@
+#Tue Jan 15 11:12:22 EST 2008
+eclipse.preferences.version=1
+encoding/<project>=ISO-8859-1
diff --git a/common/plugins/org.eclipse.jpt.common.utility/.settings/org.eclipse.jdt.core.prefs b/common/plugins/org.eclipse.jpt.common.utility/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000000..443826069d
--- /dev/null
+++ b/common/plugins/org.eclipse.jpt.common.utility/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,7 @@
+#Sun May 27 14:55:37 EDT 2007
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
+org.eclipse.jdt.core.compiler.compliance=1.5
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.5
diff --git a/common/plugins/org.eclipse.jpt.common.utility/META-INF/MANIFEST.MF b/common/plugins/org.eclipse.jpt.common.utility/META-INF/MANIFEST.MF
new file mode 100644
index 0000000000..3699c38402
--- /dev/null
+++ b/common/plugins/org.eclipse.jpt.common.utility/META-INF/MANIFEST.MF
@@ -0,0 +1,134 @@
+Manifest-Version: 1.0
+Bundle-ManifestVersion: 2
+Bundle-Name: %pluginName
+Bundle-Vendor: %providerName
+Bundle-SymbolicName: org.eclipse.jpt.common.utility
+Bundle-Version: 1.6.0.qualifier
+Bundle-Localization: plugin
+Bundle-RequiredExecutionEnvironment: J2SE-1.5
+Export-Package: org.eclipse.jpt.common.utility,
+ org.eclipse.jpt.common.utility.internal;
+ x-friends:="org.eclipse.jpt.core,
+ org.eclipse.jpt.common.core,
+ org.eclipse.jpt.common.ui,
+ org.eclipse.jpt.jpa.db,
+ org.eclipse.jpt.jpa.db.ui,
+ org.eclipse.jpt.gen,
+ org.eclipse.jpt.jaxb.core,
+ org.eclipse.jpt.jaxb.ui,
+ org.eclipse.jpt.ui",
+ org.eclipse.jpt.common.utility.internal.enumerations;
+ x-friends:="org.eclipse.jpt.core,
+ org.eclipse.jpt.common.core,
+ org.eclipse.jpt.common.ui,
+ org.eclipse.jpt.jpa.db,
+ org.eclipse.jpt.jpa.db.ui,
+ org.eclipse.jpt.gen,
+ org.eclipse.jpt.jaxb.core,
+ org.eclipse.jpt.jaxb.ui,
+ org.eclipse.jpt.ui",
+ org.eclipse.jpt.common.utility.internal.iterables;
+ x-friends:="org.eclipse.jpt.core,
+ org.eclipse.jpt.common.core,
+ org.eclipse.jpt.common.ui,
+ org.eclipse.jpt.jpa.db,
+ org.eclipse.jpt.jpa.db.ui,
+ org.eclipse.jpt.gen,
+ org.eclipse.jpt.jaxb.core,
+ org.eclipse.jpt.jaxb.ui,
+ org.eclipse.jpt.ui",
+ org.eclipse.jpt.common.utility.internal.iterators;
+ x-friends:="org.eclipse.jpt.core,
+ org.eclipse.jpt.common.core,
+ org.eclipse.jpt.common.ui,
+ org.eclipse.jpt.jpa.db,
+ org.eclipse.jpt.jpa.db.ui,
+ org.eclipse.jpt.gen,
+ org.eclipse.jpt.jaxb.core,
+ org.eclipse.jpt.jaxb.ui,
+ org.eclipse.jpt.ui",
+ org.eclipse.jpt.common.utility.internal.model;
+ x-friends:="org.eclipse.jpt.core,
+ org.eclipse.jpt.common.core,
+ org.eclipse.jpt.common.ui,
+ org.eclipse.jpt.jpa.db,
+ org.eclipse.jpt.jpa.db.ui,
+ org.eclipse.jpt.gen,
+ org.eclipse.jpt.jaxb.core,
+ org.eclipse.jpt.jaxb.ui,
+ org.eclipse.jpt.ui",
+ org.eclipse.jpt.common.utility.internal.model.listener.awt;
+ x-friends:="org.eclipse.jpt.core,
+ org.eclipse.jpt.common.core,
+ org.eclipse.jpt.common.ui,
+ org.eclipse.jpt.jpa.db,
+ org.eclipse.jpt.jpa.db.ui,
+ org.eclipse.jpt.gen,
+ org.eclipse.jpt.jaxb.core,
+ org.eclipse.jpt.jaxb.ui,
+ org.eclipse.jpt.ui",
+ org.eclipse.jpt.common.utility.internal.model.value;
+ x-friends:="org.eclipse.jpt.core,
+ org.eclipse.jpt.common.core,
+ org.eclipse.jpt.common.ui,
+ org.eclipse.jpt.jpa.db,
+ org.eclipse.jpt.jpa.db.ui,
+ org.eclipse.jpt.gen,
+ org.eclipse.jpt.jaxb.core,
+ org.eclipse.jpt.jaxb.ui,
+ org.eclipse.jpt.ui",
+ org.eclipse.jpt.common.utility.internal.model.value.prefs;
+ x-friends:="org.eclipse.jpt.core,
+ org.eclipse.jpt.common.core,
+ org.eclipse.jpt.common.ui,
+ org.eclipse.jpt.jpa.db,
+ org.eclipse.jpt.jpa.db.ui,
+ org.eclipse.jpt.gen,
+ org.eclipse.jpt.jaxb.core,
+ org.eclipse.jpt.jaxb.ui,
+ org.eclipse.jpt.ui",
+ org.eclipse.jpt.common.utility.internal.model.value.swing;
+ x-friends:="org.eclipse.jpt.core,
+ org.eclipse.jpt.common.core,
+ org.eclipse.jpt.common.ui,
+ org.eclipse.jpt.jpa.db,
+ org.eclipse.jpt.jpa.db.ui,
+ org.eclipse.jpt.gen,
+ org.eclipse.jpt.jaxb.core,
+ org.eclipse.jpt.jaxb.ui,
+ org.eclipse.jpt.ui",
+ org.eclipse.jpt.common.utility.internal.node;
+ x-friends:="org.eclipse.jpt.core,
+ org.eclipse.jpt.common.core,
+ org.eclipse.jpt.common.ui,
+ org.eclipse.jpt.jpa.db,
+ org.eclipse.jpt.jpa.db.ui,
+ org.eclipse.jpt.gen,
+ org.eclipse.jpt.jaxb.core,
+ org.eclipse.jpt.jaxb.ui,
+ org.eclipse.jpt.ui",
+ org.eclipse.jpt.common.utility.internal.swing;
+ x-friends:="org.eclipse.jpt.core,
+ org.eclipse.jpt.common.core,
+ org.eclipse.jpt.common.ui,
+ org.eclipse.jpt.jpa.db,
+ org.eclipse.jpt.jpa.db.ui,
+ org.eclipse.jpt.gen,
+ org.eclipse.jpt.jaxb.core,
+ org.eclipse.jpt.jaxb.ui,
+ org.eclipse.jpt.ui",
+ org.eclipse.jpt.common.utility.internal.synchronizers;
+ x-friends:="org.eclipse.jpt.core,
+ org.eclipse.jpt.common.core,
+ org.eclipse.jpt.common.ui,
+ org.eclipse.jpt.jpa.db,
+ org.eclipse.jpt.jpa.db.ui,
+ org.eclipse.jpt.gen,
+ org.eclipse.jpt.jaxb.core,
+ org.eclipse.jpt.jaxb.ui,
+ org.eclipse.jpt.ui",
+ org.eclipse.jpt.common.utility.model,
+ org.eclipse.jpt.common.utility.model.event,
+ org.eclipse.jpt.common.utility.model.listener,
+ org.eclipse.jpt.common.utility.model.value,
+ org.eclipse.jpt.common.utility.synchronizers
diff --git a/common/plugins/org.eclipse.jpt.common.utility/about.html b/common/plugins/org.eclipse.jpt.common.utility/about.html
new file mode 100644
index 0000000000..be534ba44f
--- /dev/null
+++ b/common/plugins/org.eclipse.jpt.common.utility/about.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN">
+<HTML>
+
+<head>
+<title>About</title>
+<meta http-equiv=Content-Type content="text/html; charset=ISO-8859-1">
+</head>
+
+<BODY lang="EN-US">
+
+<H3>About This Content</H3>
+
+<P>May 02, 2008</P>
+
+<H3>License</H3>
+
+<P>The Eclipse Foundation makes available all content in this plug-in
+("Content"). Unless otherwise indicated below, the Content is provided to you
+under the terms and conditions of the Eclipse Public License Version 1.0
+("EPL"). A copy of the EPL is available at
+<A href="http://www.eclipse.org/org/documents/epl-v10.php">http://www.eclipse.org/org/documents/epl-v10.php</A>.
+For purposes of the EPL, "Program" will mean the Content.</P>
+
+<P>If you did not receive this Content directly from the Eclipse Foundation, the
+Content is being redistributed by another party ("Redistributor") and different
+terms and conditions may apply to your use of any object code in the Content.
+Check the Redistributor's license that was provided with the Content. If no such
+license exists, contact the Redistributor. Unless otherwise indicated below, the
+terms and conditions of the EPL still apply to any source code in the Content
+and such source code may be obtained at
+<A href="http://www.eclipse.org/">http://www.eclipse.org/</A>.</P>
+
+</BODY>
+</HTML>
diff --git a/common/plugins/org.eclipse.jpt.common.utility/build.properties b/common/plugins/org.eclipse.jpt.common.utility/build.properties
new file mode 100644
index 0000000000..11ab8d42f6
--- /dev/null
+++ b/common/plugins/org.eclipse.jpt.common.utility/build.properties
@@ -0,0 +1,17 @@
+################################################################################
+# Copyright (c) 2006, 2007 Oracle. 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:
+# Oracle - initial API and implementation
+################################################################################
+javacSource=1.5
+javacTarget=1.5
+source.. = src/
+output.. = bin/
+bin.includes = .,\
+ META-INF/,\
+ about.html,\
+ plugin.properties
diff --git a/common/plugins/org.eclipse.jpt.common.utility/component.xml b/common/plugins/org.eclipse.jpt.common.utility/component.xml
new file mode 100644
index 0000000000..80c3a500b9
--- /dev/null
+++ b/common/plugins/org.eclipse.jpt.common.utility/component.xml
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (c) 2007, 2010 Oracle. 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:
+ Oracle - initial API and implementation
+ -->
+<component xmlns="http://eclipse.org/wtp/releng/tools/component-model" name="org.eclipse.jpt.common.utility"><description url=""></description><component-depends unrestricted="true"></component-depends><plugin id="org.eclipse.jpt.common.utility" fragment="false"/></component> \ No newline at end of file
diff --git a/common/plugins/org.eclipse.jpt.common.utility/plugin.properties b/common/plugins/org.eclipse.jpt.common.utility/plugin.properties
new file mode 100644
index 0000000000..c959ed0246
--- /dev/null
+++ b/common/plugins/org.eclipse.jpt.common.utility/plugin.properties
@@ -0,0 +1,24 @@
+################################################################################
+# Copyright (c) 2006, 2009 Oracle. 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:
+# Oracle - initial API and implementation
+################################################################################
+# ====================================================================
+# To code developer:
+# Do NOT change the properties between this line and the
+# "%%% END OF TRANSLATED PROPERTIES %%%" line.
+# Make a new property name, append to the end of the file and change
+# the code to use the new property.
+# ====================================================================
+
+# ====================================================================
+# %%% END OF TRANSLATED PROPERTIES %%%
+# ====================================================================
+
+pluginName = Dali Java Persistence Tools - Utility
+providerName = Eclipse Web Tools Platform
+
diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/Command.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/Command.java
new file mode 100644
index 0000000000..f152ba5fa6
--- /dev/null
+++ b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/Command.java
@@ -0,0 +1,87 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 Oracle. 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:
+ * Oracle - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.jpt.common.utility;
+
+import java.io.Serializable;
+
+/**
+ * Simple interface for implementing the GOF Command design pattern,
+ * and it doesn't carry the baggage of {@link java.lang.Runnable}.
+ * <p>
+ * Provisional API: This interface is part of an interim API that is still
+ * under development and expected to change significantly before reaching
+ * stability. It is available at this early stage to solicit feedback from
+ * pioneering adopters on the understanding that any code that uses this API
+ * will almost certainly be broken (repeatedly) as the API evolves.
+ */
+public interface Command {
+
+ /**
+ * Execute the command. The semantics of the command
+ * is determined by the contract between the client and server.
+ */
+ void execute();
+
+ /**
+ * Singleton implementation of the command interface that will do nothing
+ * when executed.
+ */
+ final class Null implements Command, Serializable {
+ public static final Command INSTANCE = new Null();
+ public static Command instance() {
+ return INSTANCE;
+ }
+ // ensure single instance
+ private Null() {
+ super();
+ }
+ public void execute() {
+ // do nothing
+ }
+ @Override
+ public String toString() {
+ return "Command.Null"; //$NON-NLS-1$
+ }
+ private static final long serialVersionUID = 1L;
+ private Object readResolve() {
+ // replace this object with the singleton
+ return INSTANCE;
+ }
+ }
+
+ /**
+ * Singleton implementation of the command interface that will throw an
+ * exception when executed.
+ */
+ final class Disabled implements Command, Serializable {
+ public static final Command INSTANCE = new Disabled();
+ public static Command instance() {
+ return INSTANCE;
+ }
+ // ensure single instance
+ private Disabled() {
+ super();
+ }
+ // throw an exception
+ public void execute() {
+ throw new UnsupportedOperationException();
+ }
+ @Override
+ public String toString() {
+ return "Command.Disabled"; //$NON-NLS-1$
+ }
+ private static final long serialVersionUID = 1L;
+ private Object readResolve() {
+ // replace this object with the singleton
+ return INSTANCE;
+ }
+ }
+
+}
diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/CommandExecutor.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/CommandExecutor.java
new file mode 100644
index 0000000000..461ab8615d
--- /dev/null
+++ b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/CommandExecutor.java
@@ -0,0 +1,61 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2009 Oracle. 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:
+ * Oracle - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.jpt.common.utility;
+
+import java.io.Serializable;
+
+/**
+ * This interface allows clients to control how a command is executed.
+ * This is useful when the server provides the command but the client provides
+ * the context (e.g. the client would like to dispatch the command to the UI
+ * thread).
+ * <p>
+ * Provisional API: This interface is part of an interim API that is still
+ * under development and expected to change significantly before reaching
+ * stability. It is available at this early stage to solicit feedback from
+ * pioneering adopters on the understanding that any code that uses this API
+ * will almost certainly be broken (repeatedly) as the API evolves.
+ */
+public interface CommandExecutor {
+
+ /**
+ * Execute the specified command.
+ */
+ void execute(Command command);
+
+
+ /**
+ * Singleton implementation of the command executor interface
+ * that simply executes the command without any sort of enhancement.
+ */
+ final class Default implements CommandExecutor, Serializable {
+ public static final CommandExecutor INSTANCE = new Default();
+ public static CommandExecutor instance() {
+ return INSTANCE;
+ }
+ // ensure single instance
+ private Default() {
+ super();
+ }
+ public void execute(Command command) {
+ command.execute();
+ }
+ @Override
+ public String toString() {
+ return "CommandExecutor.Default"; //$NON-NLS-1$
+ }
+ private static final long serialVersionUID = 1L;
+ private Object readResolve() {
+ // replace this object with the singleton
+ return INSTANCE;
+ }
+ }
+
+}
diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/Filter.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/Filter.java
new file mode 100644
index 0000000000..222185940e
--- /dev/null
+++ b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/Filter.java
@@ -0,0 +1,125 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2010 Oracle. 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:
+ * Oracle - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.jpt.common.utility;
+
+import java.io.Serializable;
+
+/**
+ * Used by various "pluggable" classes to filter objects.
+ * <p>
+ * Provisional API: This interface is part of an interim API that is still
+ * under development and expected to change significantly before reaching
+ * stability. It is available at this early stage to solicit feedback from
+ * pioneering adopters on the understanding that any code that uses this API
+ * will almost certainly be broken (repeatedly) as the API evolves.
+ *
+ * @param <T> the type of objects to be filtered
+ */
+public interface Filter<T> {
+
+ /**
+ * Return whether the specified object is "accepted" by the
+ * filter. The semantics of "accept" is determined by the
+ * contract between the client and the server.
+ */
+ boolean accept(T o);
+
+
+ /**
+ * Singleton implemetation of the filter interface that accepts all the
+ * objects (i.e. it does no filtering).
+ */
+ final class Null<S> implements Filter<S>, Serializable {
+ @SuppressWarnings("rawtypes")
+ public static final Filter INSTANCE = new Null();
+ @SuppressWarnings("unchecked")
+ public static <R> Filter<R> instance() {
+ return INSTANCE;
+ }
+ // ensure single instance
+ private Null() {
+ super();
+ }
+ // nothing is filtered - everything is accepted
+ public boolean accept(S o) {
+ return true;
+ }
+ @Override
+ public String toString() {
+ return "Filter.Null"; //$NON-NLS-1$
+ }
+ private static final long serialVersionUID = 1L;
+ private Object readResolve() {
+ // replace this object with the singleton
+ return INSTANCE;
+ }
+ }
+
+ /**
+ * Singleton implemetation of the filter interface that accepts none of the
+ * objects (i.e. it filters out all the objects).
+ */
+ final class Opaque<S> implements Filter<S>, Serializable {
+ @SuppressWarnings("rawtypes")
+ public static final Filter INSTANCE = new Opaque();
+ @SuppressWarnings("unchecked")
+ public static <R> Filter<R> instance() {
+ return INSTANCE;
+ }
+ // ensure single instance
+ private Opaque() {
+ super();
+ }
+ // everything is filtered - nothing is accepted
+ public boolean accept(S o) {
+ return false;
+ }
+ @Override
+ public String toString() {
+ return "Filter.Opaque"; //$NON-NLS-1$
+ }
+ private static final long serialVersionUID = 1L;
+ private Object readResolve() {
+ // replace this object with the singleton
+ return INSTANCE;
+ }
+ }
+
+ /**
+ * Singleton implemetation of the filter interface that throws an exception
+ * if called.
+ */
+ final class Disabled<S> implements Filter<S>, Serializable {
+ @SuppressWarnings("rawtypes")
+ public static final Filter INSTANCE = new Disabled();
+ @SuppressWarnings("unchecked")
+ public static <R> Filter<R> instance() {
+ return INSTANCE;
+ }
+ // ensure single instance
+ private Disabled() {
+ super();
+ }
+ // throw an exception
+ public boolean accept(S o) {
+ throw new UnsupportedOperationException();
+ }
+ @Override
+ public String toString() {
+ return "Filter.Disabled"; //$NON-NLS-1$
+ }
+ private static final long serialVersionUID = 1L;
+ private Object readResolve() {
+ // replace this object with the singleton
+ return INSTANCE;
+ }
+ }
+
+}
diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/IndentingPrintWriter.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/IndentingPrintWriter.java
new file mode 100644
index 0000000000..11641aa132
--- /dev/null
+++ b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/IndentingPrintWriter.java
@@ -0,0 +1,155 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2010 Oracle. 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:
+ * Oracle - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.jpt.common.utility;
+
+import java.io.PrintWriter;
+import java.io.Writer;
+
+/**
+ * Extend {@link PrintWriter} to automatically indent new lines.
+ */
+public class IndentingPrintWriter
+ extends PrintWriter
+{
+ private final String indent;
+ private int indentLevel;
+ private boolean needsIndent;
+
+ public static String DEFAULT_INDENT = "\t"; //$NON-NLS-1$
+
+
+ /**
+ * Construct a writer that indents with tabs.
+ */
+ public IndentingPrintWriter(Writer out) {
+ this(out, DEFAULT_INDENT);
+ }
+
+ /**
+ * Construct a writer that indents with the specified string.
+ */
+ public IndentingPrintWriter(Writer out, String indent) {
+ super(out);
+ this.indent = indent;
+ this.indentLevel = 0;
+ this.needsIndent = true;
+ }
+
+ /**
+ * Set flag so following line is indented.
+ */
+ @Override
+ public void println() {
+ synchronized (this.lock) {
+ super.println();
+ this.needsIndent = true;
+ }
+ }
+
+ /**
+ * Print the appropriate indent.
+ * Pre-condition: synchronized
+ */
+ private void printIndent() {
+ if (this.needsIndent) {
+ this.needsIndent = false;
+ for (int i = this.indentLevel; i-- > 0; ) {
+ this.print(this.indent);
+ }
+ }
+ }
+
+ /**
+ * Write a portion of an array of characters.
+ */
+ @Override
+ public void write(char buf[], int off, int len) {
+ synchronized (this.lock) {
+ this.printIndent();
+ super.write(buf, off, len);
+ }
+ }
+
+ /**
+ * Write a single character.
+ */
+ @Override
+ public void write(int c) {
+ synchronized (this.lock) {
+ this.printIndent();
+ super.write(c);
+ }
+ }
+
+ /**
+ * Write a portion of a string.
+ */
+ @Override
+ public void write(String s, int off, int len) {
+ synchronized (this.lock) {
+ this.printIndent();
+ super.write(s, off, len);
+ }
+ }
+
+ /**
+ * Bump the indent level.
+ */
+ public void indent() {
+ this.incrementIndentLevel();
+ }
+
+ /**
+ * Decrement the indent level.
+ */
+ public void undent() {
+ this.decrementIndentLevel();
+ }
+
+ /**
+ * Bump the indent level.
+ */
+ public void incrementIndentLevel() {
+ synchronized (this.lock) {
+ this.indentLevel++;
+ }
+ }
+
+ /**
+ * Decrement the indent level.
+ */
+ public void decrementIndentLevel() {
+ synchronized (this.lock) {
+ this.indentLevel--;
+ }
+ }
+
+ /**
+ * Return the current indent level.
+ */
+ public int getIndentLevel() {
+ synchronized (this.lock) {
+ return this.indentLevel;
+ }
+ }
+
+ /**
+ * Allow the indent level to be set directly.
+ * Return the previous indent level.
+ */
+ public int setIndentLevel(int indentLevel) {
+ synchronized (this.lock) {
+ int old = this.indentLevel;
+ this.indentLevel = indentLevel;
+ return old;
+ }
+ }
+
+}
diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/JavaType.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/JavaType.java
new file mode 100644
index 0000000000..fb019d5ebe
--- /dev/null
+++ b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/JavaType.java
@@ -0,0 +1,135 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 Oracle. 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:
+ * Oracle - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.jpt.common.utility;
+
+import java.io.PrintWriter;
+
+/**
+ * This interface describes a Java type; i.e. its "element type"
+ * and its "array depth". The element type is referenced by name,
+ * allowing us to reference classes that are not (or cannot be) loaded.
+ * <p>
+ * Provisional API: This interface is part of an interim API that is still
+ * under development and expected to change significantly before reaching
+ * stability. It is available at this early stage to solicit feedback from
+ * pioneering adopters on the understanding that any code that uses this API
+ * will almost certainly be broken (repeatedly) as the API evolves.
+ * <p>
+ * This interface is not intended to be implemented by clients.
+ */
+public interface JavaType {
+
+ /**
+ * Return the name of the type's "element type".
+ * A member type will have one or more <code>'$'</code> characters in its name.
+ */
+ String getElementTypeName();
+
+ /**
+ * Return the type's "array depth".
+ */
+ int getArrayDepth();
+
+ /**
+ * Return whether the type is an array (i.e. its "array depth" is greater
+ * than zero).
+ */
+ boolean isArray();
+
+ /**
+ * Return whether the type is a "primitive" (e.g. <code>int</code>, <code>float</code>).
+ * <p>
+ * <strong>NB:</strong> <code>void.class.isPrimitive() == true</code>
+ */
+ boolean isPrimitive();
+
+ /**
+ * Return whether the type is a "primitive wrapper" (e.g. {@link java.lang.Integer},
+ * {@link java.lang.Float}).
+ * <p>
+ * <strong>NB:</strong> <code>void.class.isPrimitive() == true</code>
+ */
+ boolean isPrimitiveWrapper();
+
+ /**
+ * Return whether the type is a "variable primitive" (e.g. <code>int</code>, <code>float</code>,
+ * but not <code>void</code>).
+ * <p>
+ * <strong>NB:</strong> variables cannot be declared <code>void</code>
+ */
+ boolean isVariablePrimitive();
+
+ /**
+ * Return whether the type is a "variable primitive wrapper" (e.g.
+ * {@link java.lang.Integer}, {@link java.lang.Float},
+ * but not {@link java.lang.Void}).
+ * <p>
+ * <strong>NB:</strong> variables cannot be declared <code>void</code>
+ */
+ boolean isVariablePrimitiveWrapper();
+
+ /**
+ * Return the class corresponding to the type's element type and array depth.
+ */
+ Class<?> getJavaClass() throws ClassNotFoundException;
+
+ /**
+ * Return the version of the type's name that matches that
+ * returned by {@link java.lang.Class#getName()}
+ * (e.g. <code>"[[J"</code>, <code>"[Ljava.lang.Object;"</code>,
+ * <code>"java.util.Map$Entry"</code>).
+ */
+ String getJavaClassName();
+
+ /**
+ * Return whether the type is equal to the specified type.
+ */
+ boolean equals(String otherElementTypeName, int otherArrayDepth);
+
+ /**
+ * Return whether the type describes to the specified type.
+ */
+ boolean describes(String className);
+
+ /**
+ * Return whether the type describes to the specified type.
+ */
+ boolean describes(Class<?> javaClass);
+
+ /**
+ * Return whether the type is equal to the specified type.
+ */
+ boolean equals(JavaType other);
+
+ /**
+ * Return the version of the type's name that can be used in source code:<ul>
+ * <li><code>"[[J"</code> => <code>"long[][]"</code>
+ * <li><code>"java.util.Map$Entry"</code> => <code>"java.util.Map.Entry"</code>
+ * </ul>
+ */
+ String declaration();
+
+ /**
+ * Append the version of the type's name that can be used in source code:<ul>
+ * <li><code>"[[J"</code> => <code>"long[][]"</code>
+ * <li><code>"java.util.Map$Entry"</code> => <code>"java.util.Map.Entry"</code>
+ * </ul>
+ */
+ void appendDeclarationTo(StringBuilder sb);
+
+ /**
+ * Print the version of the type's name that can be used in source code:<ul>
+ * <li><code>"[[J"</code> => <code>"long[][]"</code>
+ * <li><code>"java.util.Map$Entry"</code> => <code>"java.util.Map.Entry"</code>
+ * </ul>
+ */
+ void printDeclarationOn(PrintWriter pw);
+
+}
diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/MethodSignature.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/MethodSignature.java
new file mode 100644
index 0000000000..32e793a071
--- /dev/null
+++ b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/MethodSignature.java
@@ -0,0 +1,73 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 Oracle. 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:
+ * Oracle - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.jpt.common.utility;
+
+import java.io.PrintWriter;
+import java.lang.reflect.Method;
+
+/**
+ * This interface describes a Java method signature; i.e. its "name"
+ * and its "parameter types". The parameter types are referenced by name,
+ * allowing us to reference classes that are not (or cannot be) loaded.
+ * <p>
+ * Provisional API: This interface is part of an interim API that is still
+ * under development and expected to change significantly before reaching
+ * stability. It is available at this early stage to solicit feedback from
+ * pioneering adopters on the understanding that any code that uses this API
+ * will almost certainly be broken (repeatedly) as the API evolves.
+ * <p>
+ * This interface is not intended to be implemented by clients.
+ */
+public interface MethodSignature {
+
+ /**
+ * Return the method's name.
+ */
+ String getName();
+
+ /**
+ * Return the method's parameter types.
+ */
+ JavaType[] getParameterTypes();
+
+ /**
+ * Return whether the method signature describes the specified method.
+ */
+ boolean describes(Method method);
+
+ /**
+ * Return whether the method signature equals the specified signature.
+ */
+ boolean equals(String otherName, JavaType[] otherParameterTypes);
+
+ /**
+ * Return whether the method signature equals the specified signature.
+ */
+ boolean equals(MethodSignature other);
+
+ /**
+ * Return a string representation of the method's signature:<p>
+ * <code>"foo(int, java.lang.String)"</code>
+ */
+ String getSignature();
+
+ /**
+ * Append a string representation of the method's signature:<p>
+ * <code>"foo(int, java.lang.String)"</code>
+ */
+ void appendSignatureTo(StringBuilder sb);
+
+ /**
+ * Print a string representation of the method's signature:<p>
+ * <code>"foo(int, java.lang.String)"</code>
+ */
+ void printSignatureOn(PrintWriter pw);
+
+}
diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/ObjectReference.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/ObjectReference.java
new file mode 100644
index 0000000000..991c2c97d2
--- /dev/null
+++ b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/ObjectReference.java
@@ -0,0 +1,29 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Oracle. 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:
+ * Oracle - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.jpt.common.utility;
+
+/**
+ * Provide a container for passing an object that can be changed by the recipient.
+ */
+public interface ObjectReference<V>
+ extends ReadOnlyObjectReference<V>
+{
+ /**
+ * Set the value.
+ * Return the previous value.
+ */
+ V setValue(V value);
+
+ /**
+ * Set the value to <code>null</code>.
+ * Return the previous value.
+ */
+ V setNull();
+}
diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/ReadOnlyObjectReference.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/ReadOnlyObjectReference.java
new file mode 100644
index 0000000000..02c4ff6a24
--- /dev/null
+++ b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/ReadOnlyObjectReference.java
@@ -0,0 +1,43 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Oracle. 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:
+ * Oracle - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.jpt.common.utility;
+
+/**
+ * Provide a container for holding an object that cannot be changed.
+ *
+ * @see ObjectReference
+ */
+public interface ReadOnlyObjectReference<V>
+{
+ /**
+ * Return the current value.
+ */
+ V getValue();
+
+ /**
+ * Return whether the current value is equal to the specified value.
+ */
+ boolean valueEquals(Object object);
+
+ /**
+ * Return whether the current value is not equal to the specified value.
+ */
+ boolean valueNotEqual(Object object);
+
+ /**
+ * Return whether the current value is <code>null</code>.
+ */
+ boolean isNull();
+
+ /**
+ * Return whether the current value is not <code>null</code>.
+ */
+ boolean isNotNull();
+}
diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/AbstractAssociation.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/AbstractAssociation.java
new file mode 100644
index 0000000000..744d95c727
--- /dev/null
+++ b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/AbstractAssociation.java
@@ -0,0 +1,69 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 Oracle. 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:
+ * Oracle - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.jpt.common.utility.internal;
+
+/**
+ * Implement some of the methods in {@link Association} that can
+ * be defined in terms of the other methods.
+ */
+public abstract class AbstractAssociation<K, V>
+ implements Association<K, V>
+{
+ /**
+ * Default constructor.
+ */
+ protected AbstractAssociation() {
+ super();
+ }
+
+ @Override
+ public synchronized boolean equals(Object o) {
+ if ( ! (o instanceof Association<?, ?>)) {
+ return false;
+ }
+ Association<?, ?> other = (Association<?, ?>) o;
+ return this.keyEquals(other) && this.valueEquals(other);
+ }
+
+ protected boolean keyEquals(Association<?, ?> other) {
+ Object key = this.getKey();
+ return (key == null) ?
+ (other.getKey() == null) :
+ key.equals(other.getKey());
+ }
+
+ protected boolean valueEquals(Association<?, ?> other) {
+ Object value = this.getValue();
+ return (value == null) ?
+ (other.getValue() == null) :
+ value.equals(other.getValue());
+ }
+
+ @Override
+ public synchronized int hashCode() {
+ return this.keyHashCode() ^ this.valueHashCode();
+ }
+
+ protected int keyHashCode() {
+ Object key = this.getKey();
+ return (key == null) ? 0 : key.hashCode();
+ }
+
+ protected int valueHashCode() {
+ Object value = this.getValue();
+ return (value == null) ? 0 : value.hashCode();
+ }
+
+ @Override
+ public synchronized String toString() {
+ return this.getKey() + " => " + this.getValue(); //$NON-NLS-1$
+ }
+
+}
diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/ArrayTools.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/ArrayTools.java
new file mode 100644
index 0000000000..4f152023e7
--- /dev/null
+++ b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/ArrayTools.java
@@ -0,0 +1,3122 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2010 Oracle. 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:
+ * Oracle - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.jpt.common.utility.internal;
+
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Random;
+
+/**
+ * Array-related utility methods.
+ */
+public final class ArrayTools {
+ public static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
+ public static final char[] EMPTY_CHAR_ARRAY = new char[0];
+ public static final int[] EMPTY_INT_ARRAY = new int[0];
+
+ // ********** instantiation **********
+
+ /**
+ * Return a new array with the same length
+ * and the same component type as the specified array.
+ * <p>
+ * <code>Arrays.newArray(Object[] array)</code>
+ */
+ public static <E> E[] newArray(E[] array) {
+ return newArray(array, array.length);
+ }
+
+ /**
+ * Return a new array with the specified length
+ * and the same component type as the specified array.
+ * <p>
+ * <code>Arrays.newArray(Object[] array, int length)</code>
+ */
+ public static <E> E[] newArray(E[] array, int length) {
+ return newArray(componentType(array), length);
+ }
+
+ /**
+ * Return the specified array's component type, with appropriate support
+ * for generics.
+ */
+ public static <E> Class<? extends E> componentType(E[] array) {
+ Class<?> rawComponentType = array.getClass().getComponentType();
+ @SuppressWarnings("unchecked")
+ Class<? extends E> componentType = (Class<? extends E>) rawComponentType;
+ return componentType;
+ }
+
+ /**
+ * Return a new array with the specified component type and length,
+ * with appropriate support for generics. The component type cannot be a
+ * primitive type.
+ */
+ public static <E> E[] newArray(Class<? extends E> componentType, int length) {
+ if (componentType.isPrimitive()) {
+ throw new IllegalArgumentException("Array class cannot be primitive: " + componentType); //$NON-NLS-1$
+ }
+ return newArray_(componentType, length);
+ }
+
+ /**
+ * assume the component type is not a primitive class
+ */
+ @SuppressWarnings("unchecked")
+ private static <E> E[] newArray_(Class<? extends E> componentType, int length) {
+ return (E[]) ((componentType == OBJECT_CLASS) ?
+ new Object[length] :
+ Array.newInstance(componentType, length));
+ }
+ private static final Class<Object> OBJECT_CLASS = Object.class;
+
+
+ // ********** conversion **********
+
+ /**
+ * Return an array corresponding to the specified iterable.
+ * <p>
+ * <code>Iterable.toArray()</code>
+ * @see Collection#toArray()
+ */
+ public static Object[] array(Iterable<?> iterable) {
+ return array(iterable.iterator());
+ }
+
+ /**
+ * Return an array corresponding to the specified iterable.
+ * The specified iterable size is a performance hint.
+ * <p>
+ * <code>Iterable.toArray()</code>
+ * @see Collection#toArray()
+ */
+ public static Object[] array(Iterable<?> iterable, int iterableSize) {
+ return array(iterable.iterator(), iterableSize);
+ }
+
+ /**
+ * Return an array corresponding to the specified iterable;
+ * the runtime type of the returned array is that of the specified array.
+ * If the iterable fits in the specified array, it is returned therein.
+ * Otherwise, a new array is allocated with the runtime type of the
+ * specified array and the size of the iterable.
+ * <p>
+ * <code>Iterable.toArray(Object[])</code>
+ * @see Collection#toArray(Object[])
+ */
+ public static <E> E[] array(Iterable<? extends E> iterable, E[] array) {
+ return array(iterable.iterator(), array);
+ }
+
+ /**
+ * Return an array corresponding to the specified iterable;
+ * the runtime type of the returned array is that of the specified array.
+ * If the iterable fits in the specified array, it is returned therein.
+ * Otherwise, a new array is allocated with the runtime type of the
+ * specified array and the size of the iterable.
+ * The specified iterable size is a performance hint.
+ * <p>
+ * <code>Iterable.toArray(Object[])</code>
+ * @see Collection#toArray(Object[])
+ */
+ public static <E> E[] array(Iterable<? extends E> iterable, int iterableSize, E[] array) {
+ return array(iterable.iterator(), iterableSize, array);
+ }
+
+ /**
+ * Return an array corresponding to the specified iterator.
+ * <p>
+ * <code>Iterator.toArray()</code>
+ * @see Collection#toArray()
+ */
+ public static Object[] array(Iterator<?> iterator) {
+ return iterator.hasNext() ?
+ CollectionTools.list(iterator).toArray() :
+ EMPTY_OBJECT_ARRAY;
+ }
+
+ /**
+ * Return an array corresponding to the specified iterator.
+ * The specified iterator size is a performance hint.
+ * <p>
+ * <code>Iterator.toArray()</code>
+ * @see Collection#toArray()
+ */
+ public static Object[] array(Iterator<?> iterator, int iteratorSize) {
+ return iterator.hasNext() ?
+ CollectionTools.list(iterator, iteratorSize).toArray() :
+ EMPTY_OBJECT_ARRAY;
+ }
+
+ /**
+ * Return an array corresponding to the specified iterator;
+ * the runtime type of the returned array is that of the specified array.
+ * If the iterator fits in the specified array, it is returned therein.
+ * Otherwise, a new array is allocated with the runtime type of the
+ * specified array and the size of the iterator.
+ * <p>
+ * <code>Iterator.toArray(Object[])</code>
+ * @see Collection#toArray(Object[])
+ */
+ public static <E> E[] array(Iterator<? extends E> iterator, E[] array) {
+ return iterator.hasNext() ?
+ CollectionTools.list(iterator).toArray(array) :
+ emptyArray(array);
+ }
+
+ /**
+ * Return an array corresponding to the specified iterator;
+ * the runtime type of the returned array is that of the specified array.
+ * If the iterator fits in the specified array, it is returned therein.
+ * Otherwise, a new array is allocated with the runtime type of the
+ * specified array and the size of the iterator.
+ * The specified iterator size is a performance hint.
+ * <p>
+ * <code>Iterator.toArray(Object[])</code>
+ * @see Collection#toArray(Object[])
+ */
+ public static <E> E[] array(Iterator<? extends E> iterator, int iteratorSize, E[] array) {
+ return iterator.hasNext() ?
+ CollectionTools.list(iterator, iteratorSize).toArray(array) :
+ emptyArray(array);
+ }
+
+ /**
+ * If the specified array is empty, return it;
+ * otherwise, set its first element to null.
+ * @see Collection#toArray(Object[])
+ */
+ private static <E> E[] emptyArray(E[] array) {
+ return (array.length == 0) ? array : clearFirst(array);
+ }
+
+ /**
+ * Set the specified array's first element to null and and return the array.
+ * Assume the array length > 0.
+ */
+ private static <E> E[] clearFirst(E[] array) {
+ array[0] = null;
+ return array;
+ }
+
+
+ // ********** add **********
+
+ /**
+ * Return a new array containing the elements in the
+ * specified array followed by the specified object to be added.
+ * <p>
+ * <code>Arrays.add(Object[] array, Object o)</code>
+ */
+ public static <E> E[] add(E[] array, E value) {
+ int len = array.length;
+ E[] result = newArray(array, len + 1);
+ if (len > 0) {
+ System.arraycopy(array, 0, result, 0, len);
+ }
+ result[len] = value;
+ return result;
+ }
+
+ /**
+ * Return a new array containing the elements in the
+ * specified array with the specified object added at the specified index.
+ * <p>
+ * <code>Arrays.add(Object[] array, int index, Object o)</code>
+ */
+ public static <E> E[] add(E[] array, int index, E value) {
+ int len = array.length;
+ E[] result = newArray(array, len + 1);
+ if (index > 0) {
+ System.arraycopy(array, 0, result, 0, index);
+ }
+ result[index] = value;
+ if (index < len) {
+ System.arraycopy(array, index, result, index + 1, len - index);
+ }
+ return result;
+ }
+
+ /**
+ * Return a new array containing the elements in the
+ * specified array followed by the specified value to be added.
+ * <p>
+ * <code>Arrays.add(char[] array, char value)</code>
+ */
+ public static char[] add(char[] array, char value) {
+ int len = array.length;
+ char[] result = new char[len + 1];
+ if (len > 0) {
+ System.arraycopy(array, 0, result, 0, len);
+ }
+ result[len] = value;
+ return result;
+ }
+
+ /**
+ * Return a new array containing the elements in the
+ * specified array with the specified value added at the specified index.
+ * <p>
+ * <code>Arrays.add(char[] array, int index, char value)</code>
+ */
+ public static char[] add(char[] array, int index, char value) {
+ int len = array.length;
+ char[] result = new char[len + 1];
+ if (index > 0) {
+ System.arraycopy(array, 0, result, 0, index);
+ }
+ result[index] = value;
+ if (index < len) {
+ System.arraycopy(array, index, result, index + 1, len - index);
+ }
+ return result;
+ }
+
+ /**
+ * Return a new array containing the elements in the
+ * specified array followed by the specified value to be added.
+ * <p>
+ * <code>Arrays.add(int[] array, int value)</code>
+ */
+ public static int[] add(int[] array, int value) {
+ int len = array.length;
+ int[] result = new int[len + 1];
+ if (len > 0) {
+ System.arraycopy(array, 0, result, 0, len);
+ }
+ result[len] = value;
+ return result;
+ }
+
+ /**
+ * Return a new array containing the elements in the
+ * specified array with the specified value added at the specified index.
+ * <p>
+ * <code>Arrays.add(int[] array, int index, int value)</code>
+ */
+ public static int[] add(int[] array, int index, int value) {
+ int len = array.length;
+ int[] result = new int[len + 1];
+ if (index > 0) {
+ System.arraycopy(array, 0, result, 0, index);
+ }
+ result[index] = value;
+ if (index < len) {
+ System.arraycopy(array, index, result, index + 1, len - index);
+ }
+ return result;
+ }
+
+
+ // ********** add all **********
+
+ /**
+ * Return an array containing the elements in the
+ * specified array followed by the elements
+ * in the specified collection.
+ *<p>
+ * <code>Arrays.addAll(Object[] array, Collection collection)</code>
+ */
+ public static <E> E[] addAll(E[] array, Collection<? extends E> collection) {
+ return addAll(array, collection, collection.size());
+ }
+
+ /**
+ * check collection size
+ */
+ private static <E> E[] addAll(E[] array, Collection<? extends E> collection, int collectionSize) {
+ return (collectionSize == 0) ? array : addAll_(array, collection, collectionSize);
+ }
+
+ /**
+ * assume the collection is non-empty
+ */
+ private static <E> E[] addAll_(E[] array, Collection<? extends E> collection) {
+ return addAll_(array, collection, collection.size());
+ }
+
+ /**
+ * assume collection size > zero
+ */
+ private static <E> E[] addAll_(E[] array, Collection<? extends E> collection, int collectionSize) {
+ return addAll(array, collection, array.length, collectionSize);
+ }
+
+ /**
+ * assume collection size > zero; check array length
+ */
+ private static <E> E[] addAll(E[] array, Collection<? extends E> collection, int arrayLength, int collectionSize) {
+ return (arrayLength == 0) ?
+ collection.toArray(newArray(array, collectionSize)) :
+ addAll_(array, collection, arrayLength, collectionSize);
+ }
+
+ /**
+ * assume array length and collection size > zero
+ */
+ private static <E> E[] addAll_(E[] array, Collection<? extends E> collection, int arrayLength, int collectionSize) {
+ E[] result = newArray(array, arrayLength + collectionSize);
+ System.arraycopy(array, 0, result, 0, arrayLength);
+ int i = arrayLength;
+ for (E element : collection) {
+ result[i++] = element;
+ }
+ return result;
+ }
+
+ /**
+ * Return an array containing the elements in the
+ * specified array followed by the elements
+ * in the specified iterable.
+ * <p>
+ * <code>Arrays.addAll(Object[] array, Iterable iterable)</code>
+ */
+ public static <E> E[] addAll(E[] array, Iterable<? extends E> iterable) {
+ return addAll(array, iterable.iterator());
+ }
+
+ /**
+ * Return an array containing the elements in the
+ * specified array followed by the elements
+ * in the specified iterable.
+ * The specified iterable size is a performance hint.
+ * <p>
+ * <code>Arrays.addAll(Object[] array, Iterable iterable)</code>
+ */
+ public static <E> E[] addAll(E[] array, Iterable<? extends E> iterable, int iterableSize) {
+ return addAll(array, iterable.iterator(), iterableSize);
+ }
+
+ /**
+ * Return an array containing the elements in the
+ * specified array followed by the elements
+ * in the specified iterator.
+ * <p>
+ * <code>Arrays.addAll(Object[] array, Iterator iterator)</code>
+ */
+ public static <E> E[] addAll(E[] array, Iterator<? extends E> iterator) {
+ return iterator.hasNext() ? addAll_(array, CollectionTools.list(iterator)) : array;
+ }
+
+ /**
+ * Return an array containing the elements in the
+ * specified array followed by the elements
+ * in the specified iterator.
+ * The specified iterator size is a performance hint.
+ * <p>
+ * <code>Arrays.addAll(Object[] array, Iterator iterator)</code>
+ */
+ public static <E> E[] addAll(E[] array, Iterator<? extends E> iterator, int iteratorSize) {
+ return iterator.hasNext() ? addAll_(array, CollectionTools.list(iterator, iteratorSize)) : array;
+ }
+
+ /**
+ * Return an array containing the elements in the
+ * specified array 1 followed by the elements
+ * in the specified array 2.
+ * <p>
+ * <code>Arrays.addAll(Object[] array1, Object[] array2)</code>
+ */
+ public static <E> E[] addAll(E[] array1, E... array2) {
+ return addAll(array1, array2, array2.length);
+ }
+
+ /**
+ * check array 2 length
+ */
+ private static <E> E[] addAll(E[] array1, E[] array2, int array2Length) {
+ return (array2Length == 0) ? array1 : addAll_(array1, array2, array2Length);
+ }
+
+ /**
+ * assume array 2 length > 0
+ */
+ private static <E> E[] addAll_(E[] array1, E[] array2, int array2Length) {
+ return addAll(array1, array2, array1.length, array2Length);
+ }
+
+ /**
+ * assume array 2 length > 0; check array 1 length
+ */
+ private static <E> E[] addAll(E[] array1, E[] array2, int array1Length, int array2Length) {
+ return (array1Length == 0) ? array2 : addAll_(array1, array2, array1Length, array2Length);
+ }
+
+ /**
+ * assume both array lengths > 0
+ */
+ private static <E> E[] addAll_(E[] array1, E[] array2, int array1Length, int array2Length) {
+ E[] result = newArray(array1, array1Length + array2Length);
+ System.arraycopy(array1, 0, result, 0, array1Length);
+ System.arraycopy(array2, 0, result, array1Length, array2Length);
+ return result;
+ }
+
+ /**
+ * Return an array containing the elements in the
+ * first specified array with the objects in the second
+ * specified array added at the specified index.
+ * <p>
+ * <code>Arrays.addAll(Object[] array1, int index, Object[] array2)</code>
+ */
+ public static <E> E[] addAll(E[] array1, int index, E... array2) {
+ return addAll(array1, index, array2, array2.length);
+ }
+
+ /**
+ * check array 2 length
+ */
+ private static <E> E[] addAll(E[] array1, int index, E[] array2, int array2Length) {
+ return (array2Length == 0) ? array1 : addAll_(array1, index, array2, array2Length);
+ }
+
+ /**
+ * assume array 2 length > 0
+ */
+ private static <E> E[] addAll_(E[] array1, int index, E[] array2, int array2Length) {
+ return addAll(array1, index, array2, array1.length, array2Length);
+ }
+
+ /**
+ * assume array 2 length > 0; check array 1 length
+ */
+ private static <E> E[] addAll(E[] array1, int index, E[] array2, int array1Length, int array2Length) {
+ return (array1Length == 0) ?
+ array2 :
+ (index == array1Length) ? // 'array2' added to end of 'array1'
+ addAll_(array1, array2, array1Length, array2Length) :
+ addAll_(array1, index, array2, array1Length, array2Length);
+ }
+
+ /**
+ * assume both array lengths > 0 and index != array 1 length
+ */
+ private static <E> E[] addAll_(E[] array1, int index, E[] array2, int array1Length, int array2Length) {
+ E[] result = newArray(array1, array1Length + array2Length);
+ System.arraycopy(array1, 0, result, 0, index);
+ System.arraycopy(array2, 0, result, index, array2Length);
+ System.arraycopy(array1, index, result, index + array2Length, array1Length - index);
+ return result;
+ }
+
+ /**
+ * Return an array containing the elements in the
+ * specified array with the elements
+ * in the specified collection inserted at the specified index.
+ * <p>
+ * <code>Arrays.addAll(Object[] array, int index, Collection c)</code>
+ */
+ public static <E> E[] addAll(E[] array, int index, Collection<? extends E> collection) {
+ return addAll(array, index, collection, collection.size());
+ }
+
+ /**
+ * check collection size
+ */
+ private static <E> E[] addAll(E[] array, int index, Collection<? extends E> collection, int collectionSize) {
+ return (collectionSize == 0) ? array : addAll_(array, index, collection, collectionSize);
+ }
+
+ /**
+ * assume collection size > 0
+ */
+ private static <E> E[] addAll_(E[] array, int index, Collection<? extends E> collection, int collectionSize) {
+ return addAll(array, index, collection, array.length, collectionSize);
+ }
+
+ /**
+ * assume collection size > 0; check array length
+ */
+ private static <E> E[] addAll(E[] array, int index, Collection<? extends E> collection, int arrayLength, int collectionSize) {
+ if (arrayLength == 0) {
+ if (index == 0) {
+ return collection.toArray(newArray(array, collectionSize));
+ }
+ throw new IndexOutOfBoundsException("Index: " + index + ", Size: 0"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+ return (index == arrayLength) ? // 'collection' added to end of 'array'
+ addAll_(array, collection, arrayLength, collectionSize) :
+ addAll_(array, index, collection, arrayLength, collectionSize);
+ }
+
+ /**
+ * assume array length and collection size > 0 and index != array length
+ */
+ private static <E> E[] addAll_(E[] array, int index, Collection<? extends E> collection, int arrayLength, int collectionSize) {
+ E[] result = newArray(array, arrayLength + collectionSize);
+ System.arraycopy(array, 0, result, 0, index);
+ int i = index;
+ for (E item : collection) {
+ result[i++] = item;
+ }
+ System.arraycopy(array, index, result, index + collectionSize, arrayLength - index);
+ return result;
+ }
+
+ /**
+ * Return an array containing the elements in the
+ * specified array with the elements
+ * in the specified iterable inserted at the specified index.
+ * <p>
+ * <code>Arrays.addAll(Object[] array, int index, Iterable iterable)</code>
+ */
+ public static <E> E[] addAll(E[] array, int index, Iterable<? extends E> iterable) {
+ return addAll(array, index, iterable.iterator());
+ }
+
+ /**
+ * Return an array containing the elements in the
+ * specified array with the elements
+ * in the specified iterable inserted at the specified index.
+ * <p>
+ * <code>Arrays.addAll(Object[] array, int index, Iterable iterable)</code>
+ */
+ public static <E> E[] addAll(E[] array, int index, Iterable<? extends E> iterable, int iterableSize) {
+ return addAll(array, index, iterable.iterator(), iterableSize);
+ }
+
+ /**
+ * Return an array containing the elements in the
+ * specified array with the elements
+ * in the specified iterator inserted at the specified index.
+ * <p>
+ * <code>Arrays.addAll(Object[] array, int index, Iterator iterator)</code>
+ */
+ public static <E> E[] addAll(E[] array, int index, Iterator<? extends E> iterator) {
+ return iterator.hasNext() ? addAll_(array, index, CollectionTools.list(iterator)) : array;
+ }
+
+ /**
+ * Return an array containing the elements in the
+ * specified array with the elements
+ * in the specified iterator inserted at the specified index.
+ * The specified iterator size is a performance hint.
+ * <p>
+ * <code>Arrays.addAll(Object[] array, int index, Iterator iterator)</code>
+ */
+ public static <E> E[] addAll(E[] array, int index, Iterator<? extends E> iterator, int iteratorSize) {
+ return iterator.hasNext() ? addAll_(array, index, CollectionTools.list(iterator, iteratorSize)) : array;
+ }
+
+ /**
+ * assume collection is non-empty
+ */
+ private static <E> E[] addAll_(E[] array, int index, Collection<? extends E> collection) {
+ return addAll_(array, index, collection, collection.size());
+ }
+
+ /**
+ * Return an array containing the elements in the
+ * specified array 1 followed by the elements
+ * in the specified array 2.
+ * <p>
+ * <code>Arrays.addAll(char[] array1, char[] array2)</code>
+ */
+ public static char[] addAll(char[] array1, char... array2) {
+ return addAll(array1, array2, array2.length);
+ }
+
+ /**
+ * check array 2 length
+ */
+ private static char[] addAll(char[] array1, char[] array2, int array2Length) {
+ return (array2Length == 0) ? array1 : addAll_(array1, array2, array2Length);
+ }
+
+ /**
+ * assume array 2 length > 0
+ */
+ private static char[] addAll_(char[] array1, char[] array2, int array2Length) {
+ return addAll(array1, array2, array1.length, array2Length);
+ }
+
+ /**
+ * assume array 2 length > 0; check array 1 length
+ */
+ private static char[] addAll(char[] array1, char[] array2, int array1Length, int array2Length) {
+ return (array1Length == 0) ? array2 : addAll_(array1, array2, array1Length, array2Length);
+ }
+
+ /**
+ * assume both array lengths > 0
+ */
+ private static char[] addAll_(char[] array1, char[] array2, int array1Length, int array2Length) {
+ char[] result = new char[array1Length + array2Length];
+ System.arraycopy(array1, 0, result, 0, array1Length);
+ System.arraycopy(array2, 0, result, array1Length, array2Length);
+ return result;
+ }
+
+ /**
+ * Return an array containing the elements in the
+ * first specified array with the objects in the second
+ * specified array added at the specified index.
+ * <p>
+ * <code>Arrays.add(char[] array1, int index, char[] array2)</code>
+ */
+ public static char[] addAll(char[] array1, int index, char... array2) {
+ return addAll(array1, index, array2, array2.length);
+ }
+
+ /**
+ * check array 2 length
+ */
+ private static char[] addAll(char[] array1, int index, char[] array2, int array2Length) {
+ return (array2Length == 0) ? array1 : addAll_(array1, index, array2, array2Length);
+ }
+
+ /**
+ * assume array 2 length > 0
+ */
+ private static char[] addAll_(char[] array1, int index, char[] array2, int array2Length) {
+ return addAll(array1, index, array2, array1.length, array2Length);
+ }
+
+ /**
+ * assume array 2 length > 0; check array 1 length
+ */
+ private static char[] addAll(char[] array1, int index, char[] array2, int array1Length, int array2Length) {
+ return (array1Length == 0) ?
+ array2 :
+ (index == array1Length) ? // 'array2' added to end of 'array1'
+ addAll_(array1, array2, array1Length, array2Length) :
+ addAll_(array1, index, array2, array1Length, array2Length);
+ }
+
+ /**
+ * assume both array lengths > 0 and index != array 1 length
+ */
+ private static char[] addAll_(char[] array1, int index, char[] array2, int array1Length, int array2Length) {
+ char[] result = new char[array1Length + array2Length];
+ System.arraycopy(array1, 0, result, 0, index);
+ System.arraycopy(array2, 0, result, index, array2Length);
+ System.arraycopy(array1, index, result, index + array2Length, array1Length - index);
+ return result;
+ }
+
+ /**
+ * Return an array containing the elements in the
+ * specified array 1 followed by the elements
+ * in the specified array 2.
+ * <p>
+ * <code>Arrays.addAll(int[] array1, int[] array2)</code>
+ */
+ public static int[] addAll(int[] array1, int... array2) {
+ return addAll(array1, array2, array2.length);
+ }
+
+ /**
+ * check array 2 length
+ */
+ private static int[] addAll(int[] array1, int[] array2, int array2Length) {
+ return (array2Length == 0) ? array1 : addAll_(array1, array2, array2Length);
+ }
+
+ /**
+ * assume array 2 length > 0
+ */
+ private static int[] addAll_(int[] array1, int[] array2, int array2Length) {
+ return addAll(array1, array2, array1.length, array2Length);
+ }
+
+ /**
+ * assume array 2 length > 0; check array 1 length
+ */
+ private static int[] addAll(int[] array1, int[] array2, int array1Length, int array2Length) {
+ return (array1Length == 0) ? array2 : addAll_(array1, array2, array1Length, array2Length);
+ }
+
+ /**
+ * assume both array lengths > 0
+ */
+ private static int[] addAll_(int[] array1, int[] array2, int array1Length, int array2Length) {
+ int[] result = new int[array1Length + array2Length];
+ System.arraycopy(array1, 0, result, 0, array1Length);
+ System.arraycopy(array2, 0, result, array1Length, array2Length);
+ return result;
+ }
+
+ /**
+ * Return an array containing the elements in the
+ * first specified array with the objects in the second
+ * specified array added at the specified index.
+ * <p>
+ * <code>Arrays.add(int[] array1, int index, int[] array2)</code>
+ */
+ public static int[] addAll(int[] array1, int index, int... array2) {
+ return addAll(array1, index, array2, array2.length);
+ }
+
+ /**
+ * check array 2 length
+ */
+ private static int[] addAll(int[] array1, int index, int[] array2, int array2Length) {
+ return (array2Length == 0) ? array1 : addAll_(array1, index, array2, array2Length);
+ }
+
+ /**
+ * assume array 2 length > 0
+ */
+ private static int[] addAll_(int[] array1, int index, int[] array2, int array2Length) {
+ return addAll(array1, index, array2, array1.length, array2Length);
+ }
+
+ /**
+ * assume array 2 length > 0; check array 1 length
+ */
+ private static int[] addAll(int[] array1, int index, int[] array2, int array1Length, int array2Length) {
+ return (array1Length == 0) ?
+ array2 :
+ (index == array1Length) ? // 'array2' added to end of 'array1'
+ addAll_(array1, array2, array1Length, array2Length) :
+ addAll_(array1, index, array2, array1Length, array2Length);
+ }
+
+ /**
+ * assume both array lengths > 0 and index != array 1 length
+ */
+ private static int[] addAll_(int[] array1, int index, int[] array2, int array1Length, int array2Length) {
+ int[] result = new int[array1Length + array2Length];
+ System.arraycopy(array1, 0, result, 0, index);
+ System.arraycopy(array2, 0, result, index, array2Length);
+ System.arraycopy(array1, index, result, index + array2Length, array1Length - index);
+ return result;
+ }
+
+
+ // ********** clear **********
+
+ /**
+ * Return an empty array with the same component type as the specified array.
+ * <p>
+ * <code>Arrays.clear(Object[] array)</code>
+ */
+ public static <E> E[] clear(E[] array) {
+ return (array.length == 0) ? array : newArray(array, 0);
+ }
+
+
+ // ********** concatenate **********
+
+ /**
+ * Return an array containing all the elements in all the
+ * specified arrays, concatenated in the specified order.
+ * This is useful for building constant arrays out of other constant arrays.
+ * <p>
+ * <code>Arrays.concatenate(Object[]... arrays)</code>
+ */
+ public static <E> E[] concatenate(E[]... arrays) {
+ int len = 0;
+ for (E[] array : arrays) {
+ len += array.length;
+ }
+ E[] result = newArray(arrays[0], len);
+ if (len == 0) {
+ return result;
+ }
+ int current = 0;
+ for (E[] array : arrays) {
+ int arrayLength = array.length;
+ if (arrayLength > 0) {
+ System.arraycopy(array, 0, result, current, arrayLength);
+ current += arrayLength;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Return an array containing all the elements in all the
+ * specified arrays, concatenated in the specified order.
+ * This is useful for building constant arrays out other constant arrays.
+ * <p>
+ * <code>Arrays.concatenate(char[]... arrays)</code>
+ */
+ public static char[] concatenate(char[]... arrays) {
+ int len = 0;
+ for (char[] array : arrays) {
+ len += array.length;
+ }
+ if (len == 0) {
+ return EMPTY_CHAR_ARRAY;
+ }
+ char[] result = new char[len];
+ int current = 0;
+ for (char[] array : arrays) {
+ int arrayLength = array.length;
+ if (arrayLength != 0) {
+ System.arraycopy(array, 0, result, current, arrayLength);
+ current += arrayLength;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Return an array containing all the elements in all the
+ * specified arrays, concatenated in the specified order.
+ * This is useful for building constant arrays out other constant arrays.
+ * <p>
+ * <code>Arrays.concatenate(int[]... arrays)</code>
+ */
+ public static int[] concatenate(int[]... arrays) {
+ int len = 0;
+ for (int[] array : arrays) {
+ len += array.length;
+ }
+ if (len == 0) {
+ return EMPTY_INT_ARRAY;
+ }
+ int[] result = new int[len];
+ int current = 0;
+ for (int[] array : arrays) {
+ int arrayLength = array.length;
+ if (arrayLength != 0) {
+ System.arraycopy(array, 0, result, current, arrayLength);
+ current += arrayLength;
+ }
+ }
+ return result;
+ }
+
+
+ // ********** contains **********
+
+ /**
+ * Return whether the specified array contains the
+ * specified element.
+ * <p>
+ * <code>Arrays.contains(Object[] array, Object o)</code>
+ */
+ public static boolean contains(Object[] array, Object value) {
+ return contains(array, value, array.length);
+ }
+
+ /**
+ * check array length
+ */
+ private static boolean contains(Object[] array, Object value, int arrayLength) {
+ return (arrayLength == 0) ? false : contains_(array, value, arrayLength);
+ }
+
+ /**
+ * assume array length > 0
+ */
+ public static boolean contains_(Object[] array, Object value, int arrayLength) {
+ if (value == null) {
+ for (int i = arrayLength; i-- > 0; ) {
+ if (array[i] == null) {
+ return true;
+ }
+ }
+ } else {
+ for (int i = arrayLength; i-- > 0; ) {
+ if (value.equals(array[i])) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Return whether the specified array contains the
+ * specified element.
+ * <p>
+ * <code>Arrays.contains(char[] array, char value)</code>
+ */
+ public static boolean contains(char[] array, char value) {
+ return contains(array, value, array.length);
+ }
+
+ /**
+ * check array length
+ */
+ private static boolean contains(char[] array, char value, int arrayLength) {
+ return (arrayLength == 0) ? false : contains_(array, value, arrayLength);
+ }
+
+ /**
+ * assume array length > 0
+ */
+ private static boolean contains_(char[] array, char value, int arrayLength) {
+ for (int i = arrayLength; i-- > 0; ) {
+ if (array[i] == value) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Return whether the specified array contains the
+ * specified element.
+ * <p>
+ * <code>Arrays.contains(int[] array, int value)</code>
+ */
+ public static boolean contains(int[] array, int value) {
+ return contains(array, value, array.length);
+ }
+
+ /**
+ * check array length
+ */
+ private static boolean contains(int[] array, int value, int arrayLength) {
+ return (arrayLength == 0) ? false : contains_(array, value, arrayLength);
+ }
+
+ /**
+ * assume array length > 0
+ */
+ private static boolean contains_(int[] array, int value, int arrayLength) {
+ for (int i = arrayLength; i-- > 0; ) {
+ if (array[i] == value) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ // ********** contains all **********
+
+ /**
+ * Return whether the specified array contains all of the
+ * elements in the specified collection.
+ * <p>
+ * <code>Arrays.containsAll(Object[] array, Collection collection)</code>
+ */
+ public static boolean containsAll(Object[] array, Collection<?> collection) {
+ return containsAll(array, collection.iterator());
+ }
+
+ /**
+ * Return whether the specified array contains all of the
+ * elements in the specified iterable.
+ * <p>
+ * <code>Arrays.containsAll(Object[] array, Iterable iterable)</code>
+ */
+ public static boolean containsAll(Object[] array, Iterable<?> iterable) {
+ return containsAll(array, iterable.iterator());
+ }
+
+ /**
+ * Return whether the specified array contains all of the
+ * elements in the specified iterator.
+ * <p>
+ * <code>Arrays.containsAll(Object[] array, Iterator iterator)</code>
+ */
+ public static boolean containsAll(Object[] array, Iterator<?> iterator) {
+ // use hashed lookup
+ HashSet<Object> set = CollectionTools.set(array);
+ while (iterator.hasNext()) {
+ if ( ! set.contains(iterator.next())) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Return whether the specified array 1 contains all of the
+ * elements in the specified array 2.
+ * <p>
+ * <code>Arrays.containsAll(Object[] array1, Object[] array2)</code>
+ */
+ public static boolean containsAll(Object[] array1, Object... array2) {
+ // use hashed lookup
+ HashSet<Object> set = CollectionTools.set(array1);
+ for (int i = array2.length; i-- > 0; ) {
+ if ( ! set.contains(array2[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Return whether the specified array 1 contains all of the
+ * elements in the specified array 2.
+ * <p>
+ * <code>Arrays.containsAll(char[] array1, char[] array2)</code>
+ */
+ public static boolean containsAll(char[] array1, char... array2) {
+ for (int i = array2.length; i-- > 0; ) {
+ if ( ! contains(array1, array2[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Return whether the specified array 1 contains all of the
+ * elements in the specified array 2.
+ * <p>
+ * <code>Arrays.containsAll(int[] array1, int[] array2)</code>
+ */
+ public static boolean containsAll(int[] array1, int... array2) {
+ for (int i = array2.length; i-- > 0; ) {
+ if ( ! contains(array1, array2[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+
+ // ********** diff **********
+
+ /**
+ * Return the index of the first elements in the specified
+ * arrays that are different, beginning at the end.
+ * If the arrays are identical, return -1.
+ * If the arrays are different sizes, return the index of the
+ * last element in the longer array.
+ * Use the elements' {@link Object#equals(Object)} method to compare the
+ * elements.
+ */
+ public static int diffEnd(Object[] array1, Object[] array2) {
+ int len1 = array1.length;
+ int len2 = array2.length;
+ if (len1 != len2) {
+ return Math.max(len1, len2) - 1;
+ }
+ for (int i = len1 - 1; i > -1; i--) {
+ Object o = array1[i];
+ if (o == null) {
+ if (array2[i] != null) {
+ return i;
+ }
+ } else {
+ if ( ! o.equals(array2[i])) {
+ return i;
+ }
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Return the range of elements in the specified
+ * arrays that are different.
+ * If the arrays are identical, return [size, -1].
+ * Use the elements' {@link Object#equals(Object)} method to compare the
+ * elements.
+ * @see #diffStart(Object[], Object[])
+ * @see #diffEnd(Object[], Object[])
+ */
+ public static Range diffRange(Object[] array1, Object[] array2) {
+ int end = diffEnd(array1, array2);
+ if (end == -1) {
+ // the lists are identical, the start is the size of the two lists
+ return new Range(array1.length, end);
+ }
+ // the lists are different, calculate the start of the range
+ return new Range(diffStart(array1, array2), end);
+ }
+
+ /**
+ * Return the index of the first elements in the specified
+ * arrays that are different. If the arrays are identical, return
+ * the size of the two arrays (i.e. one past the last index).
+ * If the arrays are different sizes and all the elements in
+ * the shorter array match their corresponding elements in
+ * the longer array, return the size of the shorter array
+ * (i.e. one past the last index of the shorter array).
+ * Use the elements' {@link Object#equals(Object)} method to compare the
+ * elements.
+ */
+ public static int diffStart(Object[] array1, Object[] array2) {
+ int end = Math.min(array1.length, array2.length);
+ for (int i = 0; i < end; i++) {
+ Object o = array1[i];
+ if (o == null) {
+ if (array2[i] != null) {
+ return i;
+ }
+ } else {
+ if ( ! o.equals(array2[i])) {
+ return i;
+ }
+ }
+ }
+ return end;
+ }
+
+
+ // ********** identity diff **********
+
+ /**
+ * Return the index of the first elements in the specified
+ * arrays that are different, beginning at the end.
+ * If the arrays are identical, return -1.
+ * If the arrays are different sizes, return the index of the
+ * last element in the longer array.
+ * Use object identity to compare the elements.
+ */
+ public static int identityDiffEnd(Object[] array1, Object[] array2) {
+ int len1 = array1.length;
+ int len2 = array2.length;
+ if (len1 != len2) {
+ return Math.max(len1, len2) - 1;
+ }
+ for (int i = len1 - 1; i > -1; i--) {
+ if (array1[i] != array2[i]) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Return the range of elements in the specified
+ * arrays that are different.
+ * If the arrays are identical, return [size, -1].
+ * Use object identity to compare the elements.
+ * @see #identityDiffStart(Object[], Object[])
+ * @see #identityDiffEnd(Object[], Object[])
+ */
+ public static Range identityDiffRange(Object[] array1, Object[] array2) {
+ int end = identityDiffEnd(array1, array2);
+ if (end == -1) {
+ // the lists are identical, the start is the size of the two lists
+ return new Range(array1.length, end);
+ }
+ // the lists are different, calculate the start of the range
+ return new Range(identityDiffStart(array1, array2), end);
+ }
+
+ /**
+ * Return the index of the first elements in the specified
+ * arrays that are different. If the arrays are identical, return
+ * the size of the two arrays (i.e. one past the last index).
+ * If the arrays are different sizes and all the elements in
+ * the shorter array match their corresponding elements in
+ * the longer array, return the size of the shorter array
+ * (i.e. one past the last index of the shorter array).
+ * Use object identity to compare the elements.
+ */
+ public static int identityDiffStart(Object[] array1, Object[] array2) {
+ int end = Math.min(array1.length, array2.length);
+ for (int i = 0; i < end; i++) {
+ if (array1[i] != array2[i]) {
+ return i;
+ }
+ }
+ return end;
+ }
+
+
+ // ********** elements are identical **********
+
+ /**
+ * Return whether the specified arrays contain the same elements.
+ * <p>
+ * <code>Arrays.identical(Object[] array1, Object[] array2)</code>
+ */
+ public static boolean elementsAreIdentical(Object[] array1, Object[] array2) {
+ if (array1 == array2) {
+ return true;
+ }
+ if (array1 == null || array2 == null) {
+ return false;
+ }
+ int length = array1.length;
+ if (array2.length != length) {
+ return false;
+ }
+ for (int i = length; i-- > 0; ) {
+ if (array1[i] != array2[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+
+ // ********** index of **********
+
+ /**
+ * Return the index of the first occurrence of the
+ * specified element in the specified array,
+ * or return -1 if there is no such index.
+ * <p>
+ * <code>Arrays.indexOf(Object[] array, Object o)</code>
+ */
+ public static int indexOf(Object[] array, Object value) {
+ int len = array.length;
+ if (value == null) {
+ for (int i = 0; i < len; i++) {
+ if (array[i] == null) {
+ return i;
+ }
+ }
+ } else {
+ for (int i = 0; i < len; i++) {
+ if (value.equals(array[i])) {
+ return i;
+ }
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Return the index of the first occurrence of the
+ * specified element in the specified array,
+ * or return -1 if there is no such index.
+ * <p>
+ * <code>Arrays.identityIndexOf(Object[] array, Object o)</code>
+ */
+ public static int identityIndexOf(Object[] array, Object value) {
+ int len = array.length;
+ for (int i = 0; i < len; i++) {
+ if (array[i] == value) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Return the index of the first occurrence of the
+ * specified element in the specified array,
+ * or return -1 if there is no such index.
+ * <p>
+ * <code>Arrays.indexOf(char[] array, char value)</code>
+ */
+ public static int indexOf(char[] array, char value) {
+ int len = array.length;
+ for (int i = 0; i < len; i++) {
+ if (array[i] == value) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Return the index of the first occurrence of the
+ * specified element in the specified array,
+ * or return -1 if there is no such index.
+ * <p>
+ * <code>Arrays.indexOf(int[] array, int value)</code>
+ */
+ public static int indexOf(int[] array, int value) {
+ int len = array.length;
+ for (int i = 0; i < len; i++) {
+ if (array[i] == value) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+
+ // ********** insertion index of **********
+
+ /**
+ * Return the maximum index of where the specified comparable object
+ * should be inserted into the specified sorted array and still keep
+ * the array sorted.
+ */
+ public static <E extends Comparable<? super E>> int insertionIndexOf(E[] sortedArray, Comparable<E> value) {
+ int len = sortedArray.length;
+ for (int i = 0; i < len; i++) {
+ if (value.compareTo(sortedArray[i]) < 0) {
+ return i;
+ }
+ }
+ return len;
+ }
+
+ /**
+ * Return the maximum index of where the specified comparable object
+ * should be inserted into the specified sorted array and still keep
+ * the array sorted.
+ */
+ public static <E> int insertionIndexOf(E[] sortedArray, E value, Comparator<? super E> comparator) {
+ int len = sortedArray.length;
+ for (int i = 0; i < len; i++) {
+ if (comparator.compare(value, sortedArray[i]) < 0) {
+ return i;
+ }
+ }
+ return len;
+ }
+
+
+ // ********** last index of **********
+
+ /**
+ * Return the index of the last occurrence of the
+ * specified element in the specified array;
+ * return -1 if there is no such index.
+ * <p>
+ * <code>Arrays.lastIndexOf(Object[] array, Object o)</code>
+ */
+ public static int lastIndexOf(Object[] array, Object value) {
+ int len = array.length;
+ if (value == null) {
+ for (int i = len; i-- > 0; ) {
+ if (array[i] == null) {
+ return i;
+ }
+ }
+ } else {
+ for (int i = len; i-- > 0; ) {
+ if (value.equals(array[i])) {
+ return i;
+ }
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Return the index of the last occurrence of the
+ * specified element in the specified array,
+ * or return -1 if there is no such index.
+ * <p>
+ * <code>Arrays.lastIndexOf(char[] array, char value)</code>
+ */
+ public static int lastIndexOf(char[] array, char value) {
+ for (int i = array.length; i-- > 0; ) {
+ if (array[i] == value) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ /**
+ * Return the index of the last occurrence of the
+ * specified element in the specified array,
+ * or return -1 if there is no such index.
+ * <p>
+ * <code>Arrays.lastIndexOf(int[] array, int value)</code>
+ */
+ public static int lastIndexOf(int[] array, int value) {
+ for (int i = array.length; i-- > 0; ) {
+ if (array[i] == value) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+
+ // ********** min/max **********
+
+ /**
+ * Return the character from the specified array with the minimum value.
+ * <p>
+ * <code>Arrays.min(char[] array)</code>
+ */
+ public static char min(char... array) {
+ int len = array.length;
+ if (len == 0) {
+ throw new IndexOutOfBoundsException();
+ }
+ int last = len - 1;
+ char min = array[last];
+ for (int i = last; i-- > 0; ) {
+ char c = array[i];
+ if (c < min) {
+ min = c;
+ }
+ }
+ return min;
+ }
+
+ /**
+ * Return the integer from the specified array with the minimum value.
+ * <p>
+ * <code>Arrays.min(int[] array)</code>
+ */
+ public static int min(int... array) {
+ int len = array.length;
+ if (len == 0) {
+ throw new IndexOutOfBoundsException();
+ }
+ int last = len - 1;
+ int min = array[last];
+ for (int i = last; i-- > 0; ) {
+ int x = array[i];
+ if (x < min) {
+ min = x;
+ }
+ }
+ return min;
+ }
+
+ /**
+ * Return the character from the specified array with the maximum value.
+ * <p>
+ * <code>Arrays.max(char[] array)</code>
+ */
+ public static char max(char... array) {
+ int len = array.length;
+ if (len == 0) {
+ throw new IndexOutOfBoundsException();
+ }
+ int last = len - 1;
+ char max = array[last];
+ for (int i = last; i-- > 0; ) {
+ char c = array[i];
+ if (c > max) {
+ max = c;
+ }
+ }
+ return max;
+ }
+
+ /**
+ * Return the integer from the specified array with the maximum value.
+ * <p>
+ * <code>Arrays.max(int[] array)</code>
+ */
+ public static int max(int... array) {
+ int len = array.length;
+ if (len == 0) {
+ throw new IndexOutOfBoundsException();
+ }
+ int last = len - 1;
+ int max = array[last];
+ for (int i = last; i-- > 0; ) {
+ int x = array[i];
+ if (x > max) {
+ max = x;
+ }
+ }
+ return max;
+ }
+
+
+ // ********** move **********
+
+ /**
+ * Move an element from the specified source index to the specified target
+ * index. Return the altered array.
+ * <p>
+ * <code>Arrays.move(Object[] array, int targetIndex, int sourceIndex)</code>
+ */
+ public static <E> E[] move(E[] array, int targetIndex, int sourceIndex) {
+ return (targetIndex == sourceIndex) ? array : move_(array, targetIndex, sourceIndex);
+ }
+
+ /**
+ * assume target index != source index
+ */
+ private static <E> E[] move_(E[] array, int targetIndex, int sourceIndex) {
+ E temp = array[sourceIndex];
+ if (targetIndex < sourceIndex) {
+ System.arraycopy(array, targetIndex, array, targetIndex + 1, sourceIndex - targetIndex);
+ } else {
+ System.arraycopy(array, sourceIndex + 1, array, sourceIndex, targetIndex - sourceIndex);
+ }
+ array[targetIndex] = temp;
+ return array;
+ }
+
+ /**
+ * Move elements from the specified source index to the specified target
+ * index. Return the altered array.
+ * <p>
+ * <code>Arrays.move(Object[] array, int targetIndex, int sourceIndex, int length)</code>
+ */
+ public static <E> E[] move(E[] array, int targetIndex, int sourceIndex, int length) {
+ if ((targetIndex == sourceIndex) || (length == 0)) {
+ return array;
+ }
+ if (length == 1) {
+ return move_(array, targetIndex, sourceIndex);
+ }
+ E[] temp = newArray(array, length);
+ System.arraycopy(array, sourceIndex, temp, 0, length);
+ if (targetIndex < sourceIndex) {
+ System.arraycopy(array, targetIndex, array, targetIndex + length, sourceIndex - targetIndex);
+ } else {
+ System.arraycopy(array, sourceIndex + length, array, sourceIndex, targetIndex - sourceIndex);
+ }
+ System.arraycopy(temp, 0, array, targetIndex, length);
+ return array;
+ }
+
+ /**
+ * Move an element from the specified source index to the specified target
+ * index. Return the altered array.
+ * <p>
+ * <code>Arrays.move(int[] array, int targetIndex, int sourceIndex)</code>
+ */
+ public static int[] move(int[] array, int targetIndex, int sourceIndex) {
+ return (targetIndex == sourceIndex) ? array : move_(array, targetIndex, sourceIndex);
+ }
+
+ /**
+ * assume targetIndex != sourceIndex
+ */
+ private static int[] move_(int[] array, int targetIndex, int sourceIndex) {
+ int temp = array[sourceIndex];
+ if (targetIndex < sourceIndex) {
+ System.arraycopy(array, targetIndex, array, targetIndex + 1, sourceIndex - targetIndex);
+ } else {
+ System.arraycopy(array, sourceIndex + 1, array, sourceIndex, targetIndex - sourceIndex);
+ }
+ array[targetIndex] = temp;
+ return array;
+ }
+
+ /**
+ * Move elements from the specified source index to the specified target
+ * index. Return the altered array.
+ * <p>
+ * <code>Arrays.move(int[] array, int targetIndex, int sourceIndex, int length)</code>
+ */
+ public static int[] move(int[] array, int targetIndex, int sourceIndex, int length) {
+ if ((targetIndex == sourceIndex) || (length == 0)) {
+ return array;
+ }
+ if (length == 1) {
+ return move_(array, targetIndex, sourceIndex);
+ }
+ int[] temp = new int[length];
+ System.arraycopy(array, sourceIndex, temp, 0, length);
+ if (targetIndex < sourceIndex) {
+ System.arraycopy(array, targetIndex, array, targetIndex + length, sourceIndex - targetIndex);
+ } else {
+ System.arraycopy(array, sourceIndex + length, array, sourceIndex, targetIndex - sourceIndex);
+ }
+ System.arraycopy(temp, 0, array, targetIndex, length);
+ return array;
+ }
+
+ /**
+ * Move an element from the specified source index to the specified target
+ * index. Return the altered array.
+ * <p>
+ * <code>Arrays.move(char[] array, int targetIndex, int sourceIndex)</code>
+ */
+ public static char[] move(char[] array, int targetIndex, int sourceIndex) {
+ return (targetIndex == sourceIndex) ? array : move_(array, targetIndex, sourceIndex);
+ }
+
+ /**
+ * assume targetIndex != sourceIndex
+ */
+ private static char[] move_(char[] array, int targetIndex, int sourceIndex) {
+ char temp = array[sourceIndex];
+ if (targetIndex < sourceIndex) {
+ System.arraycopy(array, targetIndex, array, targetIndex + 1, sourceIndex - targetIndex);
+ } else {
+ System.arraycopy(array, sourceIndex + 1, array, sourceIndex, targetIndex - sourceIndex);
+ }
+ array[targetIndex] = temp;
+ return array;
+ }
+
+ /**
+ * Move elements from the specified source index to the specified target
+ * index. Return the altered array.
+ * <p>
+ * <code>Arrays.move(char[] array, int targetIndex, int sourceIndex, int length)</code>
+ */
+ public static char[] move(char[] array, int targetIndex, int sourceIndex, int length) {
+ if ((targetIndex == sourceIndex) || (length == 0)) {
+ return array;
+ }
+ if (length == 1) {
+ return move_(array, targetIndex, sourceIndex);
+ }
+ char[] temp = new char[length];
+ System.arraycopy(array, sourceIndex, temp, 0, length);
+ if (targetIndex < sourceIndex) {
+ System.arraycopy(array, targetIndex, array, targetIndex + length, sourceIndex - targetIndex);
+ } else {
+ System.arraycopy(array, sourceIndex + length, array, sourceIndex, targetIndex - sourceIndex);
+ }
+ System.arraycopy(temp, 0, array, targetIndex, length);
+ return array;
+ }
+
+
+ // ********** remove **********
+
+ /**
+ * Return a new array that contains the elements in the
+ * specified array with the specified element removed.
+ * <p>
+ * <code>Arrays.remove(Object[] array, Object value)</code>
+ */
+ public static <E> E[] remove(E[] array, Object value) {
+ return removeElementAtIndex(array, indexOf(array, value));
+ }
+
+ /**
+ * Return a new array that contains the elements in the
+ * specified array with the specified element removed.
+ * <p>
+ * <code>Arrays.remove(char[] array, char value)</code>
+ */
+ public static char[] remove(char[] array, char value) {
+ return removeElementAtIndex(array, indexOf(array, value));
+ }
+
+ /**
+ * Return a new array that contains the elements in the
+ * specified array with the specified element removed.
+ * <p>
+ * <code>Arrays.remove(int[] array, int value)</code>
+ */
+ public static int[] remove(int[] array, int value) {
+ return removeElementAtIndex(array, indexOf(array, value));
+ }
+
+ /**
+ * Return a new array that contains the elements in the
+ * specified array with the first element removed.
+ * <p>
+ * <code>Arrays.removeFirst(Object[] array)</code>
+ */
+ public static <E> E[] removeFirst(E[] array) {
+ return removeElementAtIndex(array, 0);
+ }
+
+ /**
+ * Return a new array that contains the elements in the
+ * specified array with the first element removed.
+ * <p>
+ * <code>Arrays.removeFirst(char[] array)</code>
+ */
+ public static char[] removeFirst(char[] array) {
+ return removeElementAtIndex(array, 0);
+ }
+
+ /**
+ * Return a new array that contains the elements in the
+ * specified array with the first element removed.
+ * <p>
+ * <code>Arrays.removeFirst(int[] array)</code>
+ */
+ public static int[] removeFirst(int[] array) {
+ return removeElementAtIndex(array, 0);
+ }
+
+ /**
+ * Return a new array that contains the elements in the
+ * specified array with the last element removed.
+ * <p>
+ * <code>Arrays.removeLast(Object[] array)</code>
+ */
+ public static <E> E[] removeLast(E[] array) {
+ return removeElementAtIndex(array, array.length - 1);
+ }
+
+ /**
+ * Return a new array that contains the elements in the
+ * specified array with the last element removed.
+ * <p>
+ * <code>Arrays.removeLast(char[] array)</code>
+ */
+ public static char[] removeLast(char[] array) {
+ return removeElementAtIndex(array, array.length - 1);
+ }
+
+ /**
+ * Return a new array that contains the elements in the
+ * specified array with the last element removed.
+ * <p>
+ * <code>Arrays.removeLast(int[] array)</code>
+ */
+ public static int[] removeLast(int[] array) {
+ return removeElementAtIndex(array, array.length - 1);
+ }
+
+
+ // ********** remove all **********
+
+ /**
+ * Remove from the specified array all the elements in
+ * the specified iterable and return the result.
+ * <p>
+ * <code>Arrays.removeAll(Object[] array, Iterable iterable)</code>
+ */
+ public static <E> E[] removeAll(E[] array, Iterable<?> iterable) {
+ return removeAll(array, iterable.iterator());
+ }
+
+ /**
+ * Remove from the specified array all the elements in
+ * the specified iterable and return the result.
+ * The specified iterable size is a performance hint.
+ * <p>
+ * <code>Arrays.removeAll(Object[] array, Iterable iterable)</code>
+ */
+ public static <E> E[] removeAll(E[] array, Iterable<?> iterable, int iterableSize) {
+ return removeAll(array, iterable.iterator(), iterableSize);
+ }
+
+ /**
+ * Remove from the specified array all the elements in
+ * the specified iterator and return the result.
+ * <p>
+ * <code>Arrays.removeAll(Object[] array, Iterator iterator)</code>
+ */
+ public static <E> E[] removeAll(E[] array, Iterator<?> iterator) {
+ // convert to a set to take advantage of hashed look-up
+ return iterator.hasNext() ? removeAll_(array, CollectionTools.set(iterator)) : array;
+ }
+
+ /**
+ * Remove from the specified array all the elements in
+ * the specified iterator and return the result.
+ * The specified iterator size is a performance hint.
+ * <p>
+ * <code>Arrays.removeAll(Object[] array, Iterator iterator)</code>
+ */
+ public static <E> E[] removeAll(E[] array, Iterator<?> iterator, int iteratorSize) {
+ // convert to a set to take advantage of hashed look-up
+ return iterator.hasNext() ? removeAll_(array, CollectionTools.set(iterator, iteratorSize)) : array;
+ }
+
+ /**
+ * Remove from the specified array all the elements in
+ * the specified collection and return the result.
+ * <p>
+ * <code>Arrays.removeAll(Object[] array, Collection collection)</code>
+ */
+ public static <E> E[] removeAll(E[] array, Collection<?> collection) {
+ return collection.isEmpty() ? array : removeAll_(array, collection);
+ }
+
+ /**
+ * assume collection is non-empty
+ */
+ private static <E> E[] removeAll_(E[] array, Collection<?> collection) {
+ return removeAll(array, collection, array.length);
+ }
+
+ /**
+ * assume collection is non-empty; check array length
+ */
+ private static <E> E[] removeAll(E[] array, Collection<?> collection, int arrayLength) {
+ return (arrayLength == 0) ? array : removeAll_(array, collection, arrayLength);
+ }
+
+ /**
+ * assume collection is non-empty and array length > 0
+ */
+ private static <E> E[] removeAll_(E[] array, Collection<?> collection, int arrayLength) {
+ // build the indices of the elements that are to remain
+ int[] indices = new int[arrayLength];
+ int j = 0;
+ for (int i = 0; i < arrayLength; i++) {
+ if ( ! collection.contains(array[i])) {
+ indices[j++] = i;
+ }
+ }
+ if (j == arrayLength) {
+ return array; // nothing was removed
+ }
+ E[] result = newArray(array, j);
+ int resultLength = result.length;
+ for (int i = 0; i < resultLength; i++) {
+ result[i] = array[indices[i]];
+ }
+ return result;
+ }
+
+ /**
+ * Remove from the first specified array all the elements in
+ * the second specified array and return the result.
+ * <p>
+ * <code>Arrays.removeAll(Object[] array1, Object[] array2)</code>
+ */
+ public static <E> E[] removeAll(E[] array1, Object... array2) {
+ // convert to a set to take advantage of hashed look-up
+ return (array2.length == 0) ? array1 : removeAll_(array1, CollectionTools.set(array2));
+ }
+
+ /**
+ * Remove from the first specified array all the elements in
+ * the second specified array and return the result.
+ * <p>
+ * <code>Arrays#removeAll(char[] array1, char[] array2)</code>
+ */
+ public static char[] removeAll(char[] array1, char... array2) {
+ if (array2.length == 0) {
+ return array1;
+ }
+ int array1Length = array1.length;
+ if (array1Length == 0) {
+ return array1;
+ }
+ int[] indices = new int[array1Length];
+ int j = 0;
+ for (int i = 0; i < array1Length; i++) {
+ if ( ! contains(array2, array1[i])) {
+ indices[j++] = i;
+ }
+ }
+ if (j == array1Length) {
+ return array1; // nothing was removed
+ }
+ char[] result = new char[j];
+ int resultLength = result.length;
+ for (int i = 0; i < resultLength; i++) {
+ result[i] = array1[indices[i]];
+ }
+ return result;
+ }
+
+ /**
+ * Remove from the first specified array all the elements in
+ * the second specified array and return the result.
+ * <p>
+ * <code>Arrays#removeAll(int[] array1, int[] array2)</code>
+ */
+ public static int[] removeAll(int[] array1, int... array2) {
+ if (array2.length == 0) {
+ return array1;
+ }
+ int array1Length = array1.length;
+ if (array1Length == 0) {
+ return array1;
+ }
+ int[] indices = new int[array1Length];
+ int j = 0;
+ for (int i = 0; i < array1Length; i++) {
+ if ( ! contains(array2, array1[i])) {
+ indices[j++] = i;
+ }
+ }
+ if (j == array1Length) {
+ return array1; // nothing was removed
+ }
+ int[] result = new int[j];
+ int resultLength = result.length;
+ for (int i = 0; i < resultLength; i++) {
+ result[i] = array1[indices[i]];
+ }
+ return result;
+ }
+
+
+ // ********** remove all occurrences **********
+
+ /**
+ * Remove from the specified array all occurrences of
+ * the specified element and return the result.
+ * <p>
+ * <code>Arrays.removeAllOccurrences(Object[] array, Object value)</code>
+ */
+ public static <E> E[] removeAllOccurrences(E[] array, Object value) {
+ int arrayLength = array.length;
+ if (arrayLength == 0) {
+ return array;
+ }
+ int[] indices = new int[arrayLength];
+ int j = 0;
+ if (value == null) {
+ for (int i = arrayLength; i-- > 0; ) {
+ if (array[i] != null) {
+ indices[j++] = i;
+ }
+ }
+ } else {
+ for (int i = array.length; i-- > 0; ) {
+ if ( ! value.equals(array[i])) {
+ indices[j++] = i;
+ }
+ }
+ }
+ if (j == arrayLength) {
+ return array; // nothing was removed
+ }
+ E[] result = newArray(array, j);
+ int resultLength = result.length;
+ for (int i = 0; i < resultLength; i++) {
+ result[i] = array[indices[i]];
+ }
+ return result;
+ }
+
+ /**
+ * Remove from the specified array all occurrences of
+ * the specified element and return the result.
+ * <p>
+ * <code>Arrays.removeAllOccurrences(char[] array, char value)</code>
+ */
+ public static char[] removeAllOccurrences(char[] array, char value) {
+ int arrayLength = array.length;
+ if (arrayLength == 0) {
+ return array;
+ }
+ int[] indices = new int[arrayLength];
+ int j = 0;
+ for (int i = arrayLength; i-- > 0; ) {
+ if (array[i] != value) {
+ indices[j++] = i;
+ }
+ }
+ if (j == arrayLength) {
+ return array; // nothing was removed
+ }
+ char[] result = new char[j];
+ int resultLength = result.length;
+ for (int i = 0; i < resultLength; i++) {
+ result[i] = array[indices[i]];
+ }
+ return result;
+ }
+
+ /**
+ * Remove from the specified array all occurrences of
+ * the specified element and return the result.
+ * <p>
+ * <code>Arrays.removeAllOccurrences(int[] array, int value)</code>
+ */
+ public static int[] removeAllOccurrences(int[] array, int value) {
+ int arrayLength = array.length;
+ if (arrayLength == 0) {
+ return array;
+ }
+ int[] indices = new int[arrayLength];
+ int j = 0;
+ for (int i = arrayLength; i-- > 0; ) {
+ if (array[i] != value) {
+ indices[j++] = i;
+ }
+ }
+ if (j == arrayLength) {
+ return array; // nothing was removed
+ }
+ int[] result = new int[j];
+ int resultLength = result.length;
+ for (int i = 0; i < resultLength; i++) {
+ result[i] = array[indices[i]];
+ }
+ return result;
+ }
+
+
+ // ********** remove duplicate elements **********
+
+ /**
+ * Remove any duplicate elements from the specified array,
+ * while maintaining the order.
+ */
+ public static <E> E[] removeDuplicateElements(E... array) {
+ int len = array.length;
+ if ((len == 0) || (len == 1)) {
+ return array;
+ }
+ ArrayList<E> temp = CollectionTools.list(array);
+ return CollectionTools.removeDuplicateElements(temp, len) ?
+ temp.toArray(newArray(array, temp.size())) :
+ array;
+ }
+
+
+ // ********** remove element at index **********
+
+ /**
+ * Return a new array that contains the elements in the
+ * specified array with the specified element removed.
+ * <p>
+ * <code>Arrays.removeElementAtIndex(Object[] array, int index)</code>
+ */
+ public static <E> E[] removeElementAtIndex(E[] array, int index) {
+ return removeElementsAtIndex(array, index, 1);
+ }
+
+ /**
+ * Return a new array that contains the elements in the
+ * specified array with the specified element removed.
+ * <p>
+ * <code>Arrays.removeElementAtIndex(char[] array, int index)</code>
+ */
+ public static char[] removeElementAtIndex(char[] array, int index) {
+ return removeElementsAtIndex(array, index, 1);
+ }
+
+ /**
+ * Return a new array that contains the elements in the
+ * specified array with the specified element removed.
+ * <p>
+ * <code>Arrays.removeElementAtIndex(int[] array, int index)</code>
+ */
+ public static int[] removeElementAtIndex(int[] array, int index) {
+ return removeElementsAtIndex(array, index, 1);
+ }
+
+
+ // ********** remove elements at index **********
+
+ /**
+ * Return a new array that contains the elements in the
+ * specified array with the specified elements removed.
+ * <p>
+ * <code>Arrays.removeElementsAtIndex(Object[] array, int index, int length)</code>
+ */
+ public static <E> E[] removeElementsAtIndex(E[] array, int index, int length) {
+ if (length == 0) {
+ return array;
+ }
+ int arrayLength = array.length;
+ int newLength = arrayLength - length;
+ E[] result = newArray(array, newLength);
+ if ((newLength == 0) && (index == 0)) {
+ return result; // performance tweak
+ }
+ if (index != 0) {
+ System.arraycopy(array, 0, result, 0, index);
+ }
+ int length2 = newLength - index;
+ if (length2 != 0) {
+ System.arraycopy(array, index + length, result, index, length2);
+ }
+ return result;
+ }
+
+ /**
+ * Return a new array that contains the elements in the
+ * specified array with the specified elements removed.
+ * <p>
+ * <code>Arrays.removeElementsAtIndex(char[] array, int index, int length)
+ */
+ public static char[] removeElementsAtIndex(char[] array, int index, int length) {
+ if (length == 0) {
+ return array;
+ }
+ int arrayLength = array.length;
+ int newLength = arrayLength - length;
+ if ((newLength == 0) && (index == 0)) {
+ return EMPTY_CHAR_ARRAY; // performance tweak
+ }
+ char[] result = new char[newLength];
+ if (index != 0) {
+ System.arraycopy(array, 0, result, 0, index);
+ }
+ int length2 = newLength - index;
+ if (length2 != 0) {
+ System.arraycopy(array, index + length, result, index, length2);
+ }
+ return result;
+ }
+
+ /**
+ * Return a new array that contains the elements in the
+ * specified array with the specified elements removed.
+ * <p>
+ * <code>Arrays.removeElementsAtIndex(int[] array, int index, int length)
+ */
+ public static int[] removeElementsAtIndex(int[] array, int index, int length) {
+ if (length == 0) {
+ return array;
+ }
+ int arrayLength = array.length;
+ int newLength = arrayLength - length;
+ if ((newLength == 0) && (index == 0)) {
+ return EMPTY_INT_ARRAY; // performance tweak
+ }
+ int[] result = new int[newLength];
+ if (index != 0) {
+ System.arraycopy(array, 0, result, 0, index);
+ }
+ int length2 = newLength - index;
+ if (length2 != 0) {
+ System.arraycopy(array, index + length, result, index, length2);
+ }
+ return result;
+ }
+
+
+ // ********** replace all **********
+
+ /**
+ * Replace all occurrences of the specified old value with
+ * the specified new value. Return the altered array.
+ * <p>
+ * <code>Arrays.replaceAll(Object[] array, Object oldValue, Object newValue)</code>
+ */
+ public static <E> E[] replaceAll(E[] array, Object oldValue, E newValue) {
+ if (oldValue == null) {
+ for (int i = array.length; i-- > 0; ) {
+ if (array[i] == null) {
+ array[i] = newValue;
+ }
+ }
+ } else {
+ for (int i = array.length; i-- > 0; ) {
+ if (oldValue.equals(array[i])) {
+ array[i] = newValue;
+ }
+ }
+ }
+ return array;
+ }
+
+ /**
+ * Replace all occurrences of the specified old value with
+ * the specified new value. Return the altered array.
+ *<p>
+ * <code> Arrays.replaceAll(int[] array, int oldValue, int newValue)</code>
+ */
+ public static int[] replaceAll(int[] array, int oldValue, int newValue) {
+ for (int i = array.length; i-- > 0; ) {
+ if (array[i] == oldValue) {
+ array[i] = newValue;
+ }
+ }
+ return array;
+ }
+
+ /**
+ * Replace all occurrences of the specified old value with
+ * the specified new value. Return the altered array.
+ * <p>
+ * <code>Arrays.replaceAll(char[] array, char oldValue, char newValue)</code>
+ */
+ public static char[] replaceAll(char[] array, char oldValue, char newValue) {
+ for (int i = array.length; i-- > 0; ) {
+ if (array[i] == oldValue) {
+ array[i] = newValue;
+ }
+ }
+ return array;
+ }
+
+
+ // ********** retain all **********
+
+ /**
+ * Retain in the specified array all the elements in
+ * the specified iterable and return the result.
+ * <p>
+ * <code>Arrays.retainAll(Object[] array, Iterable iterable)</code>
+ */
+ public static <E> E[] retainAll(E[] array, Iterable<?> iterable) {
+ int arrayLength = array.length;
+ return (arrayLength == 0) ? array : retainAll(array, arrayLength, iterable.iterator());
+ }
+
+ /**
+ * Retain in the specified array all the elements in
+ * the specified iterable and return the result.
+ * The specified iterable size is a performance hint.
+ * <p>
+ * <code>Arrays.retainAll(Object[] array, Iterable iterable)</code>
+ */
+ public static <E> E[] retainAll(E[] array, Iterable<?> iterable, int iterableSize) {
+ int arrayLength = array.length;
+ return (arrayLength == 0) ? array : retainAll(array, arrayLength, iterable.iterator(), iterableSize);
+ }
+
+ /**
+ * Retain in the specified array all the elements in
+ * the specified iterator and return the result.
+ * <p>
+ * <code>Arrays.retainAll(Object[] array, Iterator iterator)</code>
+ */
+ public static <E> E[] retainAll(E[] array, Iterator<?> iterator) {
+ int arrayLength = array.length;
+ return (arrayLength == 0) ? array : retainAll(array, arrayLength, iterator);
+ }
+
+ /**
+ * Retain in the specified array all the elements in
+ * the specified iterator and return the result.
+ * The specified iterator size is a performance hint.
+ * <p>
+ * <code>Arrays.retainAll(Object[] array, Iterator iterator)</code>
+ */
+ public static <E> E[] retainAll(E[] array, Iterator<?> iterator, int iteratorSize) {
+ int arrayLength = array.length;
+ return (arrayLength == 0) ? array : retainAll(array, arrayLength, iterator, iteratorSize);
+ }
+
+ /**
+ * assume array length > 0
+ */
+ private static <E> E[] retainAll(E[] array, int arrayLength, Iterator<?> iterator) {
+ return iterator.hasNext() ?
+ retainAll_(array, CollectionTools.set(iterator), arrayLength) :
+ newArray(array, 0);
+ }
+
+ /**
+ * assume array length > 0
+ */
+ private static <E> E[] retainAll(E[] array, int arrayLength, Iterator<?> iterator, int iteratorSize) {
+ return iterator.hasNext() ?
+ retainAll_(array, CollectionTools.set(iterator, iteratorSize), arrayLength) :
+ newArray(array, 0);
+ }
+
+ /**
+ * Retain in the specified array all the elements in
+ * the specified collection and return the result.
+ * <p>
+ * <code>Arrays.retainAll(Object[] array, Collection collection)</code>
+ */
+ public static <E> E[] retainAll(E[] array, Collection<?> collection) {
+ int arrayLength = array.length;
+ return (arrayLength == 0) ? array : retainAll(array, collection, arrayLength);
+ }
+
+ /**
+ * assume array length > 0
+ */
+ private static <E> E[] retainAll(E[] array, Collection<?> collection, int arrayLength) {
+ return collection.isEmpty() ?
+ newArray(array, 0) :
+ retainAll_(array, collection, arrayLength);
+ }
+
+ /**
+ * assume collection is non-empty and array length > 0
+ */
+ private static <E> E[] retainAll_(E[] array, Collection<?> collection, int arrayLength) {
+ int[] indices = new int[arrayLength];
+ int j = 0;
+ for (int i = 0; i < arrayLength; i++) {
+ if (collection.contains(array[i])) {
+ indices[j++] = i;
+ }
+ }
+ if (j == arrayLength) {
+ return array; // everything was retained
+ }
+ E[] result = newArray(array, j);
+ int resultLength = result.length;
+ for (int i = 0; i < resultLength; i++) {
+ result[i] = array[indices[i]];
+ }
+ return result;
+ }
+
+ /**
+ * Remove from the first specified array all the elements in
+ * the second specified array and return the result.
+ * <p>
+ * <code>Arrays.retainAll(Object[] array1, Object[] array2)</code>
+ */
+ public static <E> E[] retainAll(E[] array1, Object[] array2) {
+ int array1Length = array1.length;
+ return (array1Length == 0) ?
+ array1 :
+ (array2.length == 0) ?
+ newArray(array1, 0) :
+ retainAll(array1, CollectionTools.set(array2), array1Length);
+ }
+
+ /**
+ * Remove from the first specified array all the elements in
+ * the second specified array and return the result.
+ * <p>
+ * <code>Arrays.retainAll(char[] array1, char[] array2)</code>
+ */
+ public static char[] retainAll(char[] array1, char... array2) {
+ int array1Length = array1.length;
+ return (array1Length == 0) ? array1 : retainAll(array1, array2, array1Length);
+ }
+
+ /**
+ * assume array 1 length > 0
+ */
+ private static char[] retainAll(char[] array1, char[] array2, int array1Length) {
+ int array2Length = array2.length;
+ return (array2Length == 0) ? EMPTY_CHAR_ARRAY : retainAll(array1, array2, array1Length, array2Length);
+ }
+
+ /**
+ * assume both array lengths > 0
+ */
+ private static char[] retainAll(char[] array1, char[] array2, int array1Length, int array2Length) {
+ int[] indices = new int[array1Length];
+ int j = 0;
+ for (int i = 0; i < array1Length; i++) {
+ if (contains_(array2, array1[i], array2Length)) {
+ indices[j++] = i;
+ }
+ }
+ if (j == array1Length) {
+ return array1; // everything was retained
+ }
+ char[] result = new char[j];
+ int resultLength = result.length;
+ for (int i = 0; i < resultLength; i++) {
+ result[i] = array1[indices[i]];
+ }
+ return result;
+ }
+
+ /**
+ * Remove from the first specified array all the elements in
+ * the second specified array and return the result.
+ * <p>
+ * <code>Arrays.retainAll(int[] array1, int[] array2)</code>
+ */
+ public static int[] retainAll(int[] array1, int... array2) {
+ int array1Length = array1.length;
+ return (array1Length == 0) ? array1 : retainAll(array1, array2, array1Length);
+ }
+
+ /**
+ * assume array 1 length > 0
+ */
+ private static int[] retainAll(int[] array1, int[] array2, int array1Length) {
+ int array2Length = array2.length;
+ return (array2Length == 0) ? EMPTY_INT_ARRAY : retainAll(array1, array2, array1Length, array2Length);
+ }
+
+ /**
+ * assume both array lengths > 0
+ */
+ private static int[] retainAll(int[] array1, int[] array2, int array1Length, int array2Length) {
+ int[] indices = new int[array1Length];
+ int j = 0;
+ for (int i = 0; i < array1Length; i++) {
+ if (contains_(array2, array1[i], array2Length)) {
+ indices[j++] = i;
+ }
+ }
+ if (j == array1Length) {
+ return array1; // everything was retained
+ }
+ int[] result = new int[j];
+ int resultLength = result.length;
+ for (int i = 0; i < resultLength; i++) {
+ result[i] = array1[indices[i]];
+ }
+ return result;
+ }
+
+
+ // ********** reverse **********
+
+ /**
+ * Return the array, reversed.
+ * <p>
+ * <code>Arrays.reverse(Object... array)</code>
+ */
+ public static <E> E[] reverse(E... array) {
+ int len = array.length;
+ if ((len == 0) || (len == 1)) {
+ return array;
+ }
+ for (int i = 0, mid = len >> 1, j = len - 1; i < mid; i++, j--) {
+ swap(array, i, j);
+ }
+ return array;
+ }
+
+ /**
+ * Return the array, reversed.
+ * <p>
+ * <code>Arrays.reverse(char... array)</code>
+ */
+ public static char[] reverse(char... array) {
+ int len = array.length;
+ if ((len == 0) || (len == 1)) {
+ return array;
+ }
+ for (int i = 0, mid = len >> 1, j = len - 1; i < mid; i++, j--) {
+ swap(array, i, j);
+ }
+ return array;
+ }
+
+ /**
+ * Return the array, reversed.
+ * <p>
+ * <code>Arrays.reverse(int... array)</code>
+ */
+ public static int[] reverse(int... array) {
+ int len = array.length;
+ if ((len == 0) || (len == 1)) {
+ return array;
+ }
+ for (int i = 0, mid = len >> 1, j = len - 1; i < mid; i++, j--) {
+ swap(array, i, j);
+ }
+ return array;
+ }
+
+
+ // ********** rotate **********
+
+ /**
+ * Return the rotated array after rotating it one position.
+ * <p>
+ * <code>Arrays.rotate(Object[] array)</code>
+ */
+ public static <E> E[] rotate(E... array) {
+ return rotate(array, 1);
+ }
+
+ /**
+ * Return the rotated array after rotating it the specified distance.
+ * <p>
+ * <code>Arrays.rotate(Object[] array, int distance)</code>
+ */
+ public static <E> E[] rotate(E[] array, int distance) {
+ int len = array.length;
+ if ((len == 0) || (len == 1)) {
+ return array;
+ }
+ distance = distance % len;
+ if (distance < 0) {
+ distance += len;
+ }
+ if (distance == 0) {
+ return array;
+ }
+ for (int cycleStart = 0, nMoved = 0; nMoved != len; cycleStart++) {
+ E displaced = array[cycleStart];
+ int i = cycleStart;
+ do {
+ i += distance;
+ if (i >= len) {
+ i -= len;
+ }
+ E temp = array[i];
+ array[i] = displaced;
+ displaced = temp;
+ nMoved ++;
+ } while (i != cycleStart);
+ }
+ return array;
+ }
+
+ /**
+ * Return the rotated array after rotating it one position.
+ * <p>
+ * <code>Arrays.rotate(char[] array)</code>
+ */
+ public static char[] rotate(char... array) {
+ return rotate(array, 1);
+ }
+
+ /**
+ * Return the rotated array after rotating it the specified distance.
+ * <p>
+ * <code>Arrays.rotate(char[] array, int distance)</code>
+ */
+ public static char[] rotate(char[] array, int distance) {
+ int len = array.length;
+ if ((len == 0) || (len == 1)) {
+ return array;
+ }
+ distance = distance % len;
+ if (distance < 0) {
+ distance += len;
+ }
+ if (distance == 0) {
+ return array;
+ }
+ for (int cycleStart = 0, nMoved = 0; nMoved != len; cycleStart++) {
+ char displaced = array[cycleStart];
+ int i = cycleStart;
+ do {
+ i += distance;
+ if (i >= len) {
+ i -= len;
+ }
+ char temp = array[i];
+ array[i] = displaced;
+ displaced = temp;
+ nMoved ++;
+ } while (i != cycleStart);
+ }
+ return array;
+ }
+
+ /**
+ * Return the rotated array after rotating it one position.
+ * <p>
+ * <code>Arrays.rotate(int[] array)</code>
+ */
+ public static int[] rotate(int... array) {
+ return rotate(array, 1);
+ }
+
+ /**
+ * Return the rotated array after rotating it the specified distance.
+ * <p>
+ * <code>Arrays.rotate(int[] array, int distance)</code>
+ */
+ public static int[] rotate(int[] array, int distance) {
+ int len = array.length;
+ if ((len == 0) || (len == 1)) {
+ return array;
+ }
+ distance = distance % len;
+ if (distance < 0) {
+ distance += len;
+ }
+ if (distance == 0) {
+ return array;
+ }
+ for (int cycleStart = 0, nMoved = 0; nMoved != len; cycleStart++) {
+ int displaced = array[cycleStart];
+ int i = cycleStart;
+ do {
+ i += distance;
+ if (i >= len) {
+ i -= len;
+ }
+ int temp = array[i];
+ array[i] = displaced;
+ displaced = temp;
+ nMoved ++;
+ } while (i != cycleStart);
+ }
+ return array;
+ }
+
+
+ // ********** shuffle **********
+
+ private static final Random RANDOM = new Random();
+
+ /**
+ * Return the array after "shuffling" it.
+ * <p>
+ * <code>Arrays.shuffle(Object... array)</code>
+ */
+ public static <E> E[] shuffle(E... array) {
+ return shuffle(array, RANDOM);
+ }
+
+ /**
+ * Return the array after "shuffling" it.
+ * <p>
+ * <code>Arrays.shuffle(Object[] array, Random r)</code>
+ */
+ public static <E> E[] shuffle(E[] array, Random random) {
+ int len = array.length;
+ if ((len == 0) || (len == 1)) {
+ return array;
+ }
+ for (int i = len; i-- > 0; ) {
+ swap(array, i, random.nextInt(len));
+ }
+ return array;
+ }
+
+ /**
+ * Return the array after "shuffling" it.
+ * <p>
+ * <code>Arrays.shuffle(char... array)</code>
+ */
+ public static char[] shuffle(char... array) {
+ return shuffle(array, RANDOM);
+ }
+
+ /**
+ * Return the array after "shuffling" it.
+ * <p>
+ * <code>Arrays.shuffle(char[] array, Random r)</code>
+ */
+ public static char[] shuffle(char[] array, Random random) {
+ int len = array.length;
+ if ((len == 0) || (len == 1)) {
+ return array;
+ }
+ for (int i = len; i-- > 0; ) {
+ swap(array, i, random.nextInt(len));
+ }
+ return array;
+ }
+
+ /**
+ * Return the array after "shuffling" it.
+ * <p>
+ * <code>Arrays.shuffle(int... array)</code>
+ */
+ public static int[] shuffle(int... array) {
+ return shuffle(array, RANDOM);
+ }
+
+ /**
+ * Return the array after "shuffling" it.
+ * <p>
+ * <code>Arrays.shuffle(int[] array, Random r)</code>
+ */
+ public static int[] shuffle(int[] array, Random random) {
+ int len = array.length;
+ if ((len == 0) || (len == 1)) {
+ return array;
+ }
+ for (int i = len; i-- > 0; ) {
+ swap(array, i, random.nextInt(len));
+ }
+ return array;
+ }
+
+
+ // ********** sub-array **********
+
+ /**
+ * Return a sub-array of the specified array with elements copied from
+ * the specified range. The "from" index is inclusive; the "to" index is exclusive.
+ * <p>
+ * <code>Arrays.subArray(E[] array, int fromIndex, int toIndex)</code>
+ */
+ public static <E> E[] subArray(E[] array, int fromIndex, int toIndex) {
+ int len = toIndex - fromIndex;
+ E[] result = newArray(array, len);
+ if (len > 0) {
+ System.arraycopy(array, fromIndex, result, 0, len);
+ }
+ return result;
+ }
+
+ /**
+ * Return a sub-array of the specified array with elements copied from
+ * the specified range. The "from" index is inclusive; the "to" index is exclusive.
+ * <p>
+ * <code>Arrays.subArray(int[] array, int fromIndex, int toIndex)</code>
+ */
+ public static int[] subArray(int[] array, int fromIndex, int toIndex) {
+ int len = toIndex - fromIndex;
+ if (len == 0) {
+ return EMPTY_INT_ARRAY;
+ }
+ int[] result = new int[len];
+ System.arraycopy(array, fromIndex, result, 0, len);
+ return result;
+ }
+
+ /**
+ * Return a sub-array of the specified array with elements copied from
+ * the specified range. The "from" index is inclusive; the "to" index is exclusive.
+ * <p>
+ * <code>
+ * Arrays.subArray(char[] array, int fromIndex, int toIndex)</code>
+ * </code>
+ */
+ public static char[] subArray(char[] array, int fromIndex, int toIndex) {
+ int len = toIndex - fromIndex;
+ if (len == 0) {
+ return EMPTY_CHAR_ARRAY;
+ }
+ char[] result = new char[len];
+ System.arraycopy(array, fromIndex, result, 0, len);
+ return result;
+ }
+
+
+ // ********** swap **********
+
+ /**
+ * Return the array after the specified elements have been "swapped".
+ * <p>
+ * <code>Arrays.swap(Object[] array, int i, int j)</code>
+ */
+ public static <E> E[] swap(E[] array, int i, int j) {
+ return (i == j) ? array : swap_(array, i, j);
+ }
+
+ /**
+ * assume the indices are different
+ */
+ private static <E> E[] swap_(E[] array, int i, int j) {
+ E temp = array[i];
+ array[i] = array[j];
+ array[j] = temp;
+ return array;
+ }
+
+ /**
+ * Return the array after the specified elements have been "swapped".
+ * <p>
+ * <code>Arrays.swap(char[] array, int i, int j)</code>
+ */
+ public static char[] swap(char[] array, int i, int j) {
+ return (i == j) ? array : swap_(array, i, j);
+ }
+
+ /**
+ * assume the indices are different
+ */
+ private static char[] swap_(char[] array, int i, int j) {
+ char temp = array[i];
+ array[i] = array[j];
+ array[j] = temp;
+ return array;
+ }
+
+ /**
+ * Return the array after the specified elements have been "swapped".
+ * <p>
+ * <code>Arrays.swap(int[] array, int i, int j)</code>
+ */
+ public static int[] swap(int[] array, int i, int j) {
+ return (i == j) ? array : swap_(array, i, j);
+ }
+
+ /**
+ * assume the indices are different
+ */
+ private static int[] swap_(int[] array, int i, int j) {
+ int temp = array[i];
+ array[i] = array[j];
+ array[j] = temp;
+ return array;
+ }
+
+
+ // ********** Arrays enhancements **********
+
+ /**
+ * Return the array after it has been "filled".
+ * @see Arrays#fill(boolean[], boolean)
+ */
+ public static boolean[] fill(boolean[] array, boolean value) {
+ Arrays.fill(array, value);
+ return array;
+ }
+
+ /**
+ * Return the array after it has been "filled".
+ * @see Arrays#fill(boolean[], int, int, boolean)
+ */
+ public static boolean[] fill(boolean[] array, int fromIndex, int toIndex, boolean value) {
+ Arrays.fill(array, fromIndex, toIndex, value);
+ return array;
+ }
+
+ /**
+ * Return the array after it has been "filled".
+ * @see Arrays#fill(byte[], byte)
+ */
+ public static byte[] fill(byte[] array, byte value) {
+ Arrays.fill(array, value);
+ return array;
+ }
+
+ /**
+ * Return the array after it has been "filled".
+ * @see Arrays#fill(byte[], int, int, byte)
+ */
+ public static byte[] fill(byte[] array, int fromIndex, int toIndex, byte value) {
+ Arrays.fill(array, fromIndex, toIndex, value);
+ return array;
+ }
+
+ /**
+ * Return the array after it has been "filled".
+ * @see Arrays#fill(char[], char)
+ */
+ public static char[] fill(char[] array, char value) {
+ Arrays.fill(array, value);
+ return array;
+ }
+
+ /**
+ * Return the array after it has been "filled".
+ * @see Arrays#fill(char[], int, int, char)
+ */
+ public static char[] fill(char[] array, int fromIndex, int toIndex, char value) {
+ Arrays.fill(array, fromIndex, toIndex, value);
+ return array;
+ }
+
+ /**
+ * Return the array after it has been "filled".
+ * @see Arrays#fill(double[], double)
+ */
+ public static double[] fill(double[] array, double value) {
+ Arrays.fill(array, value);
+ return array;
+ }
+
+ /**
+ * Return the array after it has been "filled".
+ * @see Arrays#fill(double[], int, int, double)
+ */
+ public static double[] fill(double[] array, int fromIndex, int toIndex, double value) {
+ Arrays.fill(array, fromIndex, toIndex, value);
+ return array;
+ }
+
+ /**
+ * Return the array after it has been "filled".
+ * @see Arrays#fill(float[], float)
+ */
+ public static float[] fill(float[] array, float value) {
+ Arrays.fill(array, value);
+ return array;
+ }
+
+ /**
+ * Return the array after it has been "filled".
+ * @see Arrays#fill(float[], int, int, float)
+ */
+ public static float[] fill(float[] array, int fromIndex, int toIndex, float value) {
+ Arrays.fill(array, fromIndex, toIndex, value);
+ return array;
+ }
+
+ /**
+ * Return the array after it has been "filled".
+ * @see Arrays#fill(int[], int)
+ */
+ public static int[] fill(int[] array, int value) {
+ Arrays.fill(array, value);
+ return array;
+ }
+
+ /**
+ * Return the array after it has been "filled".
+ * @see Arrays#fill(int[], int, int, int)
+ */
+ public static int[] fill(int[] array, int fromIndex, int toIndex, int value) {
+ Arrays.fill(array, fromIndex, toIndex, value);
+ return array;
+ }
+
+ /**
+ * Return the array after it has been "filled".
+ * @see Arrays#fill(Object[], Object)
+ */
+ public static <E> E[] fill(E[] array, E value) {
+ Arrays.fill(array, value);
+ return array;
+ }
+
+ /**
+ * Return the array after it has been "filled".
+ * @see Arrays#fill(Object[], int, int, Object)
+ */
+ public static <E> E[] fill(E[] array, int fromIndex, int toIndex, E value) {
+ Arrays.fill(array, fromIndex, toIndex, value);
+ return array;
+ }
+
+ /**
+ * Return the array after it has been "filled".
+ * @see Arrays#fill(long[], long)
+ */
+ public static long[] fill(long[] array, long value) {
+ Arrays.fill(array, value);
+ return array;
+ }
+
+ /**
+ * Return the array after it has been "filled".
+ * @see Arrays#fill(long[], int, int, long)
+ */
+ public static long[] fill(long[] array, int fromIndex, int toIndex, long value) {
+ Arrays.fill(array, fromIndex, toIndex, value);
+ return array;
+ }
+
+ /**
+ * Return the array after it has been "filled".
+ * @see Arrays#fill(short[], short)
+ */
+ public static short[] fill(short[] array, short value) {
+ Arrays.fill(array, value);
+ return array;
+ }
+
+ /**
+ * Return the array after it has been "filled".
+ * @see Arrays#fill(short[], int, int, short)
+ */
+ public static short[] fill(short[] array, int fromIndex, int toIndex, short value) {
+ Arrays.fill(array, fromIndex, toIndex, value);
+ return array;
+ }
+
+ /**
+ * Return the array after it has been "sorted".
+ * @see Arrays#sort(byte[])
+ */
+ public static byte[] sort(byte... array) {
+ Arrays.sort(array);
+ return array;
+ }
+
+ /**
+ * Return the array after it has been "sorted".
+ * @see Arrays#sort(byte[], int, int)
+ */
+ public static byte[] sort(byte[] array, int fromIndex, int toIndex) {
+ Arrays.sort(array, fromIndex, toIndex);
+ return array;
+ }
+
+ /**
+ * Return the array after it has been "sorted".
+ * @see Arrays#sort(char[])
+ */
+ public static char[] sort(char... array) {
+ Arrays.sort(array);
+ return array;
+ }
+
+ /**
+ * Return the array after it has been "sorted".
+ * @see Arrays#sort(char[], int, int)
+ */
+ public static char[] sort(char[] array, int fromIndex, int toIndex) {
+ Arrays.sort(array, fromIndex, toIndex);
+ return array;
+ }
+
+ /**
+ * Return the array after it has been "sorted".
+ * @see Arrays#sort(double[])
+ */
+ public static double[] sort(double... array) {
+ Arrays.sort(array);
+ return array;
+ }
+
+ /**
+ * Return the array after it has been "sorted".
+ * @see Arrays#sort(double[], int, int)
+ */
+ public static double[] sort(double[] array, int fromIndex, int toIndex) {
+ Arrays.sort(array, fromIndex, toIndex);
+ return array;
+ }
+
+ /**
+ * Return the array after it has been "sorted".
+ * @see Arrays#sort(float[])
+ */
+ public static float[] sort(float... array) {
+ Arrays.sort(array);
+ return array;
+ }
+
+ /**
+ * Return the array after it has been "sorted".
+ * @see Arrays#sort(float[], int, int)
+ */
+ public static float[] sort(float[] array, int fromIndex, int toIndex) {
+ Arrays.sort(array, fromIndex, toIndex);
+ return array;
+ }
+
+ /**
+ * Return the array after it has been "sorted".
+ * @see Arrays#sort(int[])
+ */
+ public static int[] sort(int... array) {
+ Arrays.sort(array);
+ return array;
+ }
+
+ /**
+ * Return the array after it has been "sorted".
+ * @see Arrays#sort(int[], int, int)
+ */
+ public static int[] sort(int[] array, int fromIndex, int toIndex) {
+ Arrays.sort(array, fromIndex, toIndex);
+ return array;
+ }
+
+ /**
+ * Return the array after it has been "sorted".
+ * @see Arrays#sort(Object[])
+ */
+ public static <E> E[] sort(E... array) {
+ Arrays.sort(array);
+ return array;
+ }
+
+ /**
+ * Return the array after it has been "sorted".
+ * @see Arrays#sort(Object[], Comparator)
+ */
+ public static <E> E[] sort(E[] array, Comparator<? super E> comparator) {
+ Arrays.sort(array, comparator);
+ return array;
+ }
+
+ /**
+ * Return the array after it has been "sorted".
+ * @see Arrays#sort(Object[], int, int)
+ */
+ public static <E> E[] sort(E[] array, int fromIndex, int toIndex) {
+ Arrays.sort(array, fromIndex, toIndex);
+ return array;
+ }
+
+ /**
+ * Return the array after it has been "sorted".
+ * @see Arrays#sort(Object[], int, int, Comparator)
+ */
+ public static <E> E[] sort(E[] array, int fromIndex, int toIndex, Comparator<? super E> comparator) {
+ Arrays.sort(array, fromIndex, toIndex, comparator);
+ return array;
+ }
+
+ /**
+ * Return the array after it has been "sorted".
+ * @see Arrays#sort(long[])
+ */
+ public static long[] sort(long... array) {
+ Arrays.sort(array);
+ return array;
+ }
+
+ /**
+ * Return the array after it has been "sorted".
+ * @see Arrays#sort(long[], int, int)
+ */
+ public static long[] sort(long[] array, int fromIndex, int toIndex) {
+ Arrays.sort(array, fromIndex, toIndex);
+ return array;
+ }
+
+ /**
+ * Return the array after it has been "sorted".
+ * @see Arrays#sort(short[])
+ */
+ public static short[] sort(short... array) {
+ Arrays.sort(array);
+ return array;
+ }
+
+ /**
+ * Return the array after it has been "sorted".
+ * @see Arrays#sort(short[], int, int)
+ */
+ public static short[] sort(short[] array, int fromIndex, int toIndex) {
+ Arrays.sort(array, fromIndex, toIndex);
+ return array;
+ }
+
+
+ // ********** constructor **********
+
+ /**
+ * Suppress default constructor, ensuring non-instantiability.
+ */
+ private ArrayTools() {
+ super();
+ throw new UnsupportedOperationException();
+ }
+
+}
diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/Association.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/Association.java
new file mode 100644
index 0000000000..02226b4333
--- /dev/null
+++ b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/Association.java
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 Oracle. 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:
+ * Oracle - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.jpt.common.utility.internal;
+
+/**
+ * Straightforward definition of an object pairing.
+ * The key is immutable.
+ */
+public interface Association<K, V> {
+
+ /**
+ * Return the association's key.
+ */
+ K getKey();
+
+ /**
+ * Return the association's value.
+ */
+ V getValue();
+
+ /**
+ * Set the association's value.
+ * Return the previous value.
+ */
+ V setValue(V value);
+
+ /**
+ * Return true if the associations' keys and values
+ * are equal.
+ */
+ boolean equals(Object o);
+
+ /**
+ * Return a hash code based on the association's
+ * key and value.
+ */
+ int hashCode();
+
+}
diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/AsynchronousCommandExecutor.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/AsynchronousCommandExecutor.java
new file mode 100644
index 0000000000..62b2e70653
--- /dev/null
+++ b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/AsynchronousCommandExecutor.java
@@ -0,0 +1,168 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Oracle. 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:
+ * Oracle - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.jpt.common.utility.internal;
+
+import java.util.concurrent.ThreadFactory;
+
+import org.eclipse.jpt.common.utility.Command;
+
+/**
+ * This command executor will dispatch commands to be executed in a separate
+ * thread, allowing calls to {@link CommandExecutor#execute(Command)} to return
+ * immediately.
+ * <p>
+ * <strong>NB:</strong> The client-supplied commands should handle any
+ * exception appropriately (e.g. log the exception and return gracefully) so
+ * the command execution thread can continue executing.
+ */
+public class AsynchronousCommandExecutor
+ implements StatefulCommandExecutor
+{
+ /**
+ * This command queue is shared with the command execution/consumer thread.
+ * Adding a command to it will trigger the command to be executed by the
+ * command execution thread or, if another command is currently executing,
+ * to execute the new command once the currently executing command has
+ * finished executing.
+ */
+ final SynchronizedQueue<Command> commands = new SynchronizedQueue<Command>();
+
+ /**
+ * Most of the thread-related behavior is delegated to this coordinator.
+ */
+ private final ConsumerThreadCoordinator consumerThreadCoordinator;
+
+
+ // ********** construction **********
+
+ /**
+ * Construct an asynchronous command executor.
+ * Use simple JDK thread(s) for the command execution thread(s).
+ * Allow the command execution thread(s) to be assigned JDK-generated names.
+ */
+ public AsynchronousCommandExecutor() {
+ this(SimpleThreadFactory.instance(), null);
+ }
+
+ /**
+ * Construct an asynchronous command executor.
+ * Use the specified thread factory to construct the command execution thread(s).
+ * Allow the command execution thread(s) to be assigned JDK-generated names.
+ */
+ public AsynchronousCommandExecutor(ThreadFactory threadFactory) {
+ this(threadFactory, null);
+ }
+
+ /**
+ * Construct an asynchronous command executor.
+ * Use simple JDK thread(s) for the command execution thread(s).
+ * Assign the command execution thread(s) the specified name.
+ */
+ public AsynchronousCommandExecutor(String threadName) {
+ this(SimpleThreadFactory.instance(), threadName);
+ }
+
+ /**
+ * Construct an asynchronous command executor.
+ * Assign the command execution thread(s) the specified name.
+ */
+ public AsynchronousCommandExecutor(ThreadFactory threadFactory, String threadName) {
+ super();
+ this.consumerThreadCoordinator = this.buildConsumerThreadCoordinator(threadFactory, threadName);
+ }
+
+ private ConsumerThreadCoordinator buildConsumerThreadCoordinator(ThreadFactory threadFactory, String threadName) {
+ return new ConsumerThreadCoordinator(this.buildConsumer(), threadFactory, threadName);
+ }
+
+ private ConsumerThreadCoordinator.Consumer buildConsumer() {
+ return new Consumer();
+ }
+
+
+ // ********** CallbackStatefulCommandExecutor implementation **********
+
+ /**
+ * Build and start the command execution/consumer thread.
+ * <p>
+ * Note: We don't clear the command queue here; so if a command has been
+ * added to the queue <em>before</em> getting here, the first command will
+ * be executed promptly (albeit, asynchronously).
+ * The command queue will be non-empty if:<ul>
+ * <li>{@link #execute(Command)} was called after the command executor was
+ * constructed but before {@link #start()} was called; or
+ * <li>{@link #execute(Command)} was called after {@link #stop()} was called
+ * but before {@link #start()} was called (to restart the command executor); or
+ * <li>{@link #stop()} was called when there were still outstanding commands
+ * remaining in the command queue
+ * </ul>
+ */
+ public void start() {
+ this.consumerThreadCoordinator.start();
+ }
+
+ /**
+ * Put the specified command on the command queue, to be consumed by the
+ * command execution thread.
+ */
+ public void execute(Command command) {
+ this.commands.enqueue(command);
+ }
+
+ /**
+ * Interrupt the command execution thread so that it stops executing at the
+ * end of the current command. Suspend the current thread until
+ * the command execution thread is finished executing. If any uncaught
+ * exceptions were thrown while the execution thread was executing,
+ * wrap them in a composite exception and throw the composite exception.
+ */
+ public void stop() {
+ this.consumerThreadCoordinator.stop();
+ }
+
+
+ // ********** consumer **********
+
+ /**
+ * This implementation of {@link ConsumerThreadCoordinator.Consumer}
+ * will execute the commands enqueued by the asynchronous command executor.
+ * It will wait until the shared command queue is non-empty to begin executing the
+ * commands in the queue. Once a comand is executed, the thread will quiesce until
+ * another command is placed in the command queue. If a new command is
+ * enqueued during the execution of another command (either recursively by
+ * the command itself or by another thread),
+ * the new command will be executed immediately after the currently
+ * executing command is finished.
+ * Stop the thread by calling {@link Thread#interrupt()}.
+ */
+ class Consumer
+ implements ConsumerThreadCoordinator.Consumer
+ {
+ Consumer() {
+ super();
+ }
+
+ /**
+ * Wait until a command has been placed in the queue.
+ */
+ public void waitForProducer() throws InterruptedException {
+ AsynchronousCommandExecutor.this.commands.waitUntilNotEmpty();
+ }
+
+ /**
+ * Execute the first command in the queue and notify our listeners.
+ */
+ public void execute() {
+ AsynchronousCommandExecutor.this.commands.dequeue().execute();
+ }
+
+ }
+
+}
diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/Bag.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/Bag.java
new file mode 100644
index 0000000000..0cecf9718c
--- /dev/null
+++ b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/Bag.java
@@ -0,0 +1,197 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2010 Oracle. 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:
+ * Oracle - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.jpt.common.utility.internal;
+
+import java.io.Serializable;
+import java.util.AbstractCollection;
+import java.util.Iterator;
+import org.eclipse.jpt.common.utility.internal.iterators.EmptyIterator;
+
+/**
+ * A collection that allows duplicate elements.
+ * <p>
+ * The <code>Bag</code> interface places additional stipulations,
+ * beyond those inherited from the {@link java.util.Collection} interface,
+ * on the contracts of the {@link #equals(Object)} and {@link #hashCode()} methods.
+ *
+ * @see HashBag
+ */
+
+public interface Bag<E> extends java.util.Collection<E> {
+
+ /**
+ * Compares the specified object with this bag for equality. Returns
+ * <code>true</code> if the specified object is also a bag, the two bags
+ * have the same size, and every member of the specified bag is
+ * contained in this bag with the same number of occurrences (or equivalently,
+ * every member of this bag is contained in the specified bag with the same
+ * number of occurrences). This definition ensures that the
+ * equals method works properly across different implementations of the
+ * bag interface.
+ */
+ boolean equals(Object o);
+
+ /**
+ * Returns the hash code value for this bag. The hash code of a bag is
+ * defined to be the sum of the hash codes of the elements in the bag,
+ * where the hashcode of a <code>null</code> element is defined to be zero.
+ * This ensures that <code>b1.equals(b2)</code> implies that
+ * <code>b1.hashCode() == b2.hashCode()</code> for any two bags
+ * <code>b1</code> and <code>b2</code>, as required by the general
+ * contract of the {@link Object#hashCode()} method.
+ */
+ int hashCode();
+
+ /**
+ * Return the number of times the specified object occurs in the bag.
+ */
+ int count(Object o);
+
+ /**
+ * Add the specified object the specified number of times to the bag.
+ * Return whether the bag changed.
+ */
+ boolean add(E o, int count);
+
+ /**
+ * Remove the specified number of occurrences of the specified object
+ * from the bag. Return whether the bag changed.
+ */
+ boolean remove(Object o, int count);
+
+ /**
+ * Return an iterator that returns each item in the bag
+ * once and only once, irrespective of how many times
+ * the item was added to the bag.
+ */
+ java.util.Iterator<E> uniqueIterator();
+
+ /**
+ * Return the number of unique items in the bag.
+ */
+ int uniqueCount();
+
+ /**
+ * Return an iterator that returns an entry for each item in the bag
+ * once and only once, irrespective of how many times
+ * the item was added to the bag. The entry will indicate the item's
+ * count.
+ */
+ java.util.Iterator<Entry<E>> entries();
+
+
+ /**
+ * A bag entry (element-count pair).
+ * The {@link Bag#entries()} method returns an iterator whose
+ * elements are of this class. The <em>only</em> way to obtain a reference
+ * to a bag entry is from the iterator returned by this method. These
+ * <code>Bag.Entry</code> objects are valid <em>only</em> for the duration
+ * of the iteration; more formally, the behavior of a bag entry is
+ * undefined if the backing bag has been modified after the entry was
+ * returned by the iterator, except through the {@link #setCount(int)}
+ * operation on the bag entry.
+ */
+ interface Entry<E> {
+
+ /**
+ * Return the entry's element.
+ */
+ E getElement();
+
+ /**
+ * Return entry's count; i.e. the number of times the entry's element
+ * occurs in the bag.
+ * @see Bag#count(Object)
+ */
+ int getCount();
+
+ /**
+ * Set the entry's count; i.e. the number of times the entry's element
+ * occurs in the bag. The new count must be a positive number.
+ * Return the previous count of the entry's element.
+ * NB: Use {@link Iterator#remove()} to set the
+ * count to zero.
+ */
+ int setCount(int count);
+
+ /**
+ * Return whether the entry is equal to the specified object;
+ * i.e. the specified object is a <code>Bag.Entry</code> and its
+ * element and count are the same as the entry's.
+ */
+ boolean equals(Object obj);
+
+ /**
+ * Return the entry's hash code.
+ */
+ int hashCode();
+
+ }
+
+
+ final class Empty<E> extends AbstractCollection<E> implements Bag<E>, Serializable {
+ @SuppressWarnings("rawtypes")
+ public static final Bag INSTANCE = new Empty();
+ @SuppressWarnings("unchecked")
+ public static <T> Bag<T> instance() {
+ return INSTANCE;
+ }
+ // ensure single instance
+ private Empty() {
+ super();
+ }
+ @Override
+ public Iterator<E> iterator() {
+ return EmptyIterator.instance();
+ }
+ @Override
+ public int size() {
+ return 0;
+ }
+ public Iterator<E> uniqueIterator() {
+ return EmptyIterator.instance();
+ }
+ public int uniqueCount() {
+ return 0;
+ }
+ public int count(Object o) {
+ return 0;
+ }
+ public Iterator<Bag.Entry<E>> entries() {
+ return EmptyIterator.instance();
+ }
+ public boolean remove(Object o, int count) {
+ return false;
+ }
+ public boolean add(E o, int count) {
+ throw new UnsupportedOperationException();
+ }
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+ if ( ! (o instanceof Bag<?>)) {
+ return false;
+ }
+ return ((Bag<?>) o).size() == 0;
+ }
+ @Override
+ public int hashCode() {
+ return 0;
+ }
+ private static final long serialVersionUID = 1L;
+ private Object readResolve() {
+ // replace this object with the singleton
+ return INSTANCE;
+ }
+ }
+
+}
diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/BidiFilter.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/BidiFilter.java
new file mode 100644
index 0000000000..2894793ffd
--- /dev/null
+++ b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/BidiFilter.java
@@ -0,0 +1,122 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 Oracle. 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:
+ * Oracle - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.jpt.common.utility.internal;
+
+import java.io.Serializable;
+
+import org.eclipse.jpt.common.utility.Filter;
+
+/**
+ * Used by various "pluggable" classes to filter objects
+ * in both directions.
+ *
+ * If anyone can come up with a better class name
+ * and/or method name, I would love to hear it. ~bjv
+ */
+public interface BidiFilter<T> extends Filter<T> {
+
+ /**
+ * Return whether the specified object is "accepted" by the
+ * "reverse" filter. What that means is determined by the client.
+ */
+ boolean reverseAccept(T o);
+
+
+ final class Null<S> implements BidiFilter<S>, Serializable {
+ @SuppressWarnings("rawtypes")
+ public static final BidiFilter INSTANCE = new Null();
+ @SuppressWarnings("unchecked")
+ public static <R> BidiFilter<R> instance() {
+ return INSTANCE;
+ }
+ // ensure single instance
+ private Null() {
+ super();
+ }
+ // nothing is filtered - everything is accepted
+ public boolean accept(S o) {
+ return true;
+ }
+ // nothing is "reverse-filtered" - everything is accepted
+ public boolean reverseAccept(S o) {
+ return true;
+ }
+ @Override
+ public String toString() {
+ return "BidiFilter.Null"; //$NON-NLS-1$
+ }
+ private static final long serialVersionUID = 1L;
+ private Object readResolve() {
+ // replace this object with the singleton
+ return INSTANCE;
+ }
+ }
+
+ final class Opaque<S> implements BidiFilter<S>, Serializable {
+ @SuppressWarnings("rawtypes")
+ public static final BidiFilter INSTANCE = new Opaque();
+ @SuppressWarnings("unchecked")
+ public static <R> BidiFilter<R> instance() {
+ return INSTANCE;
+ }
+ // ensure single instance
+ private Opaque() {
+ super();
+ }
+ // everything is filtered - nothing is accepted
+ public boolean accept(S o) {
+ return false;
+ }
+ // everything is "reverse-filtered" - nothing is accepted
+ public boolean reverseAccept(S o) {
+ return false;
+ }
+ @Override
+ public String toString() {
+ return "BidiFilter.Opaque"; //$NON-NLS-1$
+ }
+ private static final long serialVersionUID = 1L;
+ private Object readResolve() {
+ // replace this object with the singleton
+ return INSTANCE;
+ }
+ }
+
+ final class Disabled<S> implements BidiFilter<S>, Serializable {
+ @SuppressWarnings("rawtypes")
+ public static final BidiFilter INSTANCE = new Disabled();
+ @SuppressWarnings("unchecked")
+ public static <R> BidiFilter<R> instance() {
+ return INSTANCE;
+ }
+ // ensure single instance
+ private Disabled() {
+ super();
+ }
+ // throw an exception
+ public boolean accept(S o) {
+ throw new UnsupportedOperationException();
+ }
+ // throw an exception
+ public boolean reverseAccept(S o) {
+ throw new UnsupportedOperationException();
+ }
+ @Override
+ public String toString() {
+ return "BidiFilter.Disabled"; //$NON-NLS-1$
+ }
+ private static final long serialVersionUID = 1L;
+ private Object readResolve() {
+ // replace this object with the singleton
+ return INSTANCE;
+ }
+ }
+
+}
diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/BidiStringConverter.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/BidiStringConverter.java
new file mode 100644
index 0000000000..056b29ae9f
--- /dev/null
+++ b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/BidiStringConverter.java
@@ -0,0 +1,149 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 Oracle. 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:
+ * Oracle - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.jpt.common.utility.internal;
+
+import java.io.Serializable;
+
+/**
+ * Used by various "pluggable" classes to transform objects
+ * into strings and vice versa.
+ *
+ * If anyone can come up with a better class name
+ * and/or method name, I would love to hear it. ~bjv
+ */
+public interface BidiStringConverter<T> extends StringConverter<T> {
+
+ /**
+ * Convert the specified string into an object.
+ * The semantics of "convert to object" is determined by the
+ * contract between the client and the server.
+ * Typically, if the string is null, null is returned.
+ */
+ T convertToObject(String s);
+
+
+ final class Default<S> implements BidiStringConverter<S>, Serializable {
+ @SuppressWarnings("rawtypes")
+ public static final BidiStringConverter INSTANCE = new Default();
+ @SuppressWarnings("unchecked")
+ public static <R> BidiStringConverter<R> instance() {
+ return INSTANCE;
+ }
+ // ensure single instance
+ private Default() {
+ super();
+ }
+ // simply return the object's #toString() result
+ public String convertToString(S o) {
+ return (o == null) ? null : o.toString();
+ }
+ // simply return the string
+ @SuppressWarnings("unchecked")
+ public S convertToObject(String s) {
+ return (S) s;
+ }
+ @Override
+ public String toString() {
+ return "BidiStringConverter.Default"; //$NON-NLS-1$
+ }
+ private static final long serialVersionUID = 1L;
+ private Object readResolve() {
+ // replace this object with the singleton
+ return INSTANCE;
+ }
+ }
+
+ final class Disabled<S> implements BidiStringConverter<S>, Serializable {
+ @SuppressWarnings("rawtypes")
+ public static final BidiStringConverter INSTANCE = new Disabled();
+ @SuppressWarnings("unchecked")
+ public static <R> BidiStringConverter<R> instance() {
+ return INSTANCE;
+ }
+ // ensure single instance
+ private Disabled() {
+ super();
+ }
+ // throw an exception
+ public String convertToString(S o) {
+ throw new UnsupportedOperationException();
+ }
+ // throw an exception
+ public S convertToObject(String s) {
+ throw new UnsupportedOperationException();
+ }
+ @Override
+ public String toString() {
+ return "BidiStringConverter.Disabled"; //$NON-NLS-1$
+ }
+ private static final long serialVersionUID = 1L;
+ private Object readResolve() {
+ // replace this object with the singleton
+ return INSTANCE;
+ }
+ }
+
+ final class BooleanConverter implements BidiStringConverter<Boolean>, Serializable {
+ public static final BidiStringConverter<Boolean> INSTANCE = new BooleanConverter();
+ public static BidiStringConverter<Boolean> instance() {
+ return INSTANCE;
+ }
+ // ensure single instance
+ private BooleanConverter() {
+ super();
+ }
+ /** Return "true" if the Boolean is true, otherwise return "false". */
+ public String convertToString(Boolean b) {
+ return (b == null) ? null : b.toString();
+ }
+ /** Return Boolean.TRUE if the string is "true" (case-insensitive), otherwise return Boolean.FALSE. */
+ public Boolean convertToObject(String s) {
+ return (s == null) ? null : Boolean.valueOf(s);
+ }
+ @Override
+ public String toString() {
+ return "BidiStringConverter.BooleanConverter"; //$NON-NLS-1$
+ }
+ private static final long serialVersionUID = 1L;
+ private Object readResolve() {
+ // replace this object with the singleton
+ return INSTANCE;
+ }
+ }
+
+ final class IntegerConverter implements BidiStringConverter<Integer>, Serializable {
+ public static final BidiStringConverter<Integer> INSTANCE = new IntegerConverter();
+ public static BidiStringConverter<Integer> instance() {
+ return INSTANCE;
+ }
+ // ensure single instance
+ private IntegerConverter() {
+ super();
+ }
+ /** Integer's #toString() works well. */
+ public String convertToString(Integer integer) {
+ return (integer == null) ? null : integer.toString();
+ }
+ /** Convert the string to an Integer, if possible. */
+ public Integer convertToObject(String s) {
+ return (s == null) ? null : Integer.valueOf(s);
+ }
+ @Override
+ public String toString() {
+ return "BidiStringConverter.IntegerConverter"; //$NON-NLS-1$
+ }
+ private static final long serialVersionUID = 1L;
+ private Object readResolve() {
+ // replace this object with the singleton
+ return INSTANCE;
+ }
+ }
+
+}
diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/BidiTransformer.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/BidiTransformer.java
new file mode 100644
index 0000000000..c105d9d50a
--- /dev/null
+++ b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/BidiTransformer.java
@@ -0,0 +1,93 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 Oracle. 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:
+ * Oracle - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.jpt.common.utility.internal;
+
+import java.io.Serializable;
+
+/**
+ * Used by various "pluggable" classes to transform objects
+ * in both directions.
+ *
+ * If anyone can come up with a better class name
+ * and/or method name, I would love to hear it. ~bjv
+ */
+public interface BidiTransformer<T1, T2> extends Transformer<T1, T2> {
+
+ /**
+ * Return the "reverse-transformed" object.
+ * The semantics of "reverse-transform" is determined by the
+ * contract between the client and the server.
+ */
+ T1 reverseTransform(T2 o);
+
+
+ final class Null<S1, S2> implements BidiTransformer<S1, S2>, Serializable {
+ @SuppressWarnings("rawtypes")
+ public static final BidiTransformer INSTANCE = new Null();
+ @SuppressWarnings("unchecked")
+ public static <R1, R2> BidiTransformer<R1, R2> instance() {
+ return INSTANCE;
+ }
+ // ensure single instance
+ private Null() {
+ super();
+ }
+ // simply return the object, unchanged
+ @SuppressWarnings("unchecked")
+ public S2 transform(S1 o) {
+ return (S2) o;
+ }
+ // simply return the object, unchanged
+ @SuppressWarnings("unchecked")
+ public S1 reverseTransform(S2 o) {
+ return (S1) o;
+ }
+ @Override
+ public String toString() {
+ return "BidiTransformer.Null"; //$NON-NLS-1$
+ }
+ private static final long serialVersionUID = 1L;
+ private Object readResolve() {
+ // replace this object with the singleton
+ return INSTANCE;
+ }
+ }
+
+ final class Disabled<S1, S2> implements BidiTransformer<S1, S2>, Serializable {
+ @SuppressWarnings("rawtypes")
+ public static final BidiTransformer INSTANCE = new Disabled();
+ @SuppressWarnings("unchecked")
+ public static <R1, R2> BidiTransformer<R1, R2> instance() {
+ return INSTANCE;
+ }
+ // ensure single instance
+ private Disabled() {
+ super();
+ }
+ // throw an exception
+ public S2 transform(S1 o) {
+ throw new UnsupportedOperationException();
+ }
+ // throw an exception
+ public S1 reverseTransform(S2 o) {
+ throw new UnsupportedOperationException();
+ }
+ @Override
+ public String toString() {
+ return "BidiTransformer.Disabled"; //$NON-NLS-1$
+ }
+ private static final long serialVersionUID = 1L;
+ private Object readResolve() {
+ // replace this object with the singleton
+ return INSTANCE;
+ }
+ }
+
+}
diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/BitTools.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/BitTools.java
new file mode 100644
index 0000000000..20cbf7c9f0
--- /dev/null
+++ b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/BitTools.java
@@ -0,0 +1,214 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2009 Oracle. 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:
+ * Oracle - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.jpt.common.utility.internal;
+
+/**
+ * Assorted bit tools
+ */
+public final class BitTools {
+
+ /**
+ * Return whether the specified 'flags' has the specified
+ * 'flagToCheck' set.
+ */
+ public static boolean flagIsSet(int flags, int flagToCheck) {
+ return allFlagsAreSet(flags, flagToCheck);
+ }
+
+ /**
+ * Return whether the specified 'flags' has the specified
+ * 'flagToCheck' turned off.
+ */
+ public static boolean flagIsOff(int flags, int flagToCheck) {
+ return allFlagsAreOff(flags, flagToCheck);
+ }
+
+ /**
+ * Return whether the specified 'flags' has ONLY the specified
+ * 'flagToCheck' set.
+ */
+ public static boolean onlyFlagIsSet(int flags, int flagToCheck) {
+ return onlyFlagsAreSet(flags, flagToCheck);
+ }
+
+ /**
+ * Return whether the specified 'flags' has ONLY the specified
+ * 'flagToCheck' turned off.
+ */
+ public static boolean onlyFlagIsOff(int flags, int flagToCheck) {
+ return onlyFlagsAreOff(flags, flagToCheck);
+ }
+
+ /**
+ * Return whether the specified 'flags' has all the specified
+ * 'flagsToCheck' set.
+ */
+ public static boolean allFlagsAreSet(int flags, int flagsToCheck) {
+ return (flags & flagsToCheck) == flagsToCheck;
+ }
+
+ /**
+ * Return whether the specified 'flags' has all the specified
+ * 'flagsToCheck' turned off.
+ */
+ public static boolean allFlagsAreOff(int flags, int flagsToCheck) {
+ return (flags & flagsToCheck) == 0;
+ }
+
+ /**
+ * Return whether the specified 'flags' has ONLY the specified
+ * 'flagsToCheck' set.
+ */
+ public static boolean onlyFlagsAreSet(int flags, int flagsToCheck) {
+ return allFlagsAreSet(flags, flagsToCheck) && allFlagsAreOff(flags, ~flagsToCheck);
+ }
+
+ /**
+ * Return whether the specified 'flags' has ONLY the specified
+ * 'flagsToCheck' turned off.
+ */
+ public static boolean onlyFlagsAreOff(int flags, int flagsToCheck) {
+ return allFlagsAreOff(flags, flagsToCheck) && allFlagsAreSet(flags, ~flagsToCheck);
+ }
+
+ /**
+ * Return whether the specified 'flags' has any one of the specified
+ * 'flagsToCheck' set.
+ */
+ public static boolean anyFlagsAreSet(int flags, int flagsToCheck) {
+ return (flags & flagsToCheck) != 0;
+ }
+
+ /**
+ * Return whether the specified 'flags' has any one of the specified
+ * 'flagsToCheck' turned off.
+ */
+ public static boolean anyFlagsAreOff(int flags, int flagsToCheck) {
+ return (flags & flagsToCheck) != flagsToCheck;
+ }
+
+ /**
+ * Return whether the specified 'flags' has all the specified
+ * 'flagsToCheck' set.
+ */
+ public static boolean allFlagsAreSet(int flags, int... flagsToCheck) {
+ for (int i = flagsToCheck.length; i-- > 0; ) {
+ if ( ! allFlagsAreSet(flags, flagsToCheck[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Return whether the specified 'flags' has all the specified
+ * 'flagsToCheck' turned off.
+ */
+ public static boolean allFlagsAreOff(int flags, int... flagsToCheck) {
+ for (int i = flagsToCheck.length; i-- > 0; ) {
+ if ( ! allFlagsAreOff(flags, flagsToCheck[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Return whether the specified 'flags' has ONLY the specified
+ * 'flagsToCheck' set.
+ */
+ public static boolean onlyFlagsAreSet(int flags, int... flagsToCheck) {
+ int combinedFlags = orFlags(flagsToCheck);
+ return allFlagsAreSet(flags, combinedFlags) && allFlagsAreOff(flags, ~combinedFlags);
+ }
+
+ /**
+ * Return whether the specified 'flags' has ONLY the specified
+ * 'flagsToCheck' turned off.
+ */
+ public static boolean onlyFlagsAreOff(int flags, int... flagsToCheck) {
+ int combinedFlags = orFlags(flagsToCheck);
+ return allFlagsAreOff(flags, combinedFlags) && allFlagsAreSet(flags, ~combinedFlags);
+ }
+
+ /**
+ * Return whether the specified 'flags' has any one of the specified
+ * 'flagsToCheck' set.
+ */
+ public static boolean anyFlagsAreSet(int flags, int... flagsToCheck) {
+ for (int i = flagsToCheck.length; i-- > 0; ) {
+ if (anyFlagsAreSet(flags, flagsToCheck[i])) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Return whether the specified 'flags' has any one of the specified
+ * 'flagsToCheck' turned off.
+ */
+ public static boolean anyFlagsAreOff(int flags, int... flagsToCheck) {
+ for (int i = flagsToCheck.length; i-- > 0; ) {
+ if (anyFlagsAreOff(flags, flagsToCheck[i])) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * OR all the specified 'flags' together and return the result.
+ */
+ public static int orFlags(int... flags) {
+ int last = flags.length - 1;
+ int result = flags[last];
+ for (int i = last; i-- > 0; ) {
+ result |= flags[i];
+ }
+ return result;
+ }
+
+ /**
+ * AND all the specified 'flags' together and return the result.
+ */
+ public static int andFlags(int... flags) {
+ int last = flags.length - 1;
+ int result = flags[last];
+ for (int i = last; i-- > 0; ) {
+ result &= flags[i];
+ }
+ return result;
+ }
+
+ /**
+ * XOR all the specified 'flags' together and return the result.
+ */
+ public static int xorFlags(int... flags) {
+ int last = flags.length - 1;
+ int result = flags[last];
+ for (int i = last; i-- > 0; ) {
+ result ^= flags[i];
+ }
+ return result;
+ }
+
+
+ // ********** constructor **********
+
+ /**
+ * Suppress default constructor, ensuring non-instantiability.
+ */
+ private BitTools() {
+ super();
+ throw new UnsupportedOperationException();
+ }
+
+}
diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/BooleanReference.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/BooleanReference.java
new file mode 100644
index 0000000000..9d114257d1
--- /dev/null
+++ b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/BooleanReference.java
@@ -0,0 +1,48 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Oracle. 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:
+ * Oracle - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.jpt.common.utility.internal;
+
+/**
+ * Interface for a container for passing a flag that can be changed by
+ * the recipient.
+ */
+public interface BooleanReference
+ extends ReadOnlyBooleanReference
+{
+ /**
+ * Set the <code>boolean</code> value.
+ * Return the previous value.
+ */
+ boolean setValue(boolean value);
+
+ /**
+ * Set the <code>boolean</code> value to the NOT of its current value.
+ * Return the new value.
+ */
+ boolean flip();
+
+ /**
+ * Set the <code>boolean</code> value to the NOT of the specified value.
+ * Return the previous value.
+ */
+ boolean setNot(boolean v);
+
+ /**
+ * Set the <code>boolean</code> value to <code>true</code>.
+ * Return the previous value.
+ */
+ boolean setTrue();
+
+ /**
+ * Set the <code>boolean</code> value to <code>false</code>.
+ * Return the previous value.
+ */
+ boolean setFalse();
+}
diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/BooleanTools.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/BooleanTools.java
new file mode 100644
index 0000000000..783c0f1299
--- /dev/null
+++ b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/BooleanTools.java
@@ -0,0 +1,105 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Oracle. 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:
+ * Oracle - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.jpt.common.utility.internal;
+
+/**
+ * Assorted "Capital-B Boolean" operations.
+ */
+ // commented code is just playing around with building *everything* from NAND
+public final class BooleanTools {
+
+ /**
+ * Return the NOT of the specified Boolean.
+ * Boolean#not()
+ */
+ public static Boolean not(Boolean b) {
+ return Boolean.valueOf( ! b.booleanValue());
+// return nand(b, b);
+ }
+
+ /**
+ * Return the AND of the specified Booleans.
+ * Boolean#and(Boolean)
+ */
+ public static Boolean and(Boolean b1, Boolean b2) {
+ return Boolean.valueOf(b1.booleanValue() && b2.booleanValue());
+// Boolean nand = nand(b1, b2);
+// return nand(nand, nand);
+ }
+
+ /**
+ * Return the OR of the specified Booleans.
+ * Boolean#or(Boolean)
+ */
+ public static Boolean or(Boolean b1, Boolean b2) {
+ return Boolean.valueOf(b1.booleanValue() || b2.booleanValue());
+// Boolean nand = nand(b1, b2);
+// Boolean xor = nand(nand(b1, nand), nand(b2, nand));
+// Boolean and = nand(nand, nand);
+// Boolean nand2 = nand(xor, and);
+// return nand(nand(xor, nand2), nand(and, nand2));
+ }
+
+ /**
+ * Return the XOR of the specified Booleans.
+ * Boolean#xor(Boolean)
+ */
+ public static Boolean xor(Boolean b1, Boolean b2) {
+ return and(or(b1, b2), nand(b1, b2));
+// Boolean nand = nand(b1, b2);
+// return nand(nand(b1, nand), nand(b2, nand));
+ }
+
+ /**
+ * Return the NAND of the specified Booleans.
+ * Boolean#nand(Boolean)
+ */
+ public static Boolean nand(Boolean b1, Boolean b2) {
+ return not(and(b1, b2));
+// return Boolean.valueOf( ! (b1.booleanValue() && b2.booleanValue()));
+ }
+
+ /**
+ * Return the NOR of the specified Booleans.
+ * Boolean#nor(Boolean)
+ */
+ public static Boolean nor(Boolean b1, Boolean b2) {
+ return not(or(b1, b2));
+// Boolean nand = nand(b1, b2);
+// Boolean xor = nand(nand(b1, nand), nand(b2, nand));
+// Boolean and = nand(nand, nand);
+// Boolean nand2 = nand(xor, and);
+// Boolean nand3 = nand(nand(xor, nand2), nand(and, nand2));
+// return nand(nand3, nand3);
+ }
+
+ /**
+ * Return the XNOR of the specified Booleans.
+ * Boolean#xnor(Boolean)
+ */
+ public static Boolean xnor(Boolean b1, Boolean b2) {
+ return not(xor(b1, b2));
+// Boolean nand = nand(b1, b2);
+// Boolean xor = nand(nand(b1, nand), nand(b2, nand));
+// return nand(xor, xor);
+ }
+
+
+ // ********** constructor **********
+
+ /**
+ * Suppress default constructor, ensuring non-instantiability.
+ */
+ private BooleanTools() {
+ super();
+ throw new UnsupportedOperationException();
+ }
+
+}
diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/ClassName.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/ClassName.java
new file mode 100644
index 0000000000..aaf4be351c
--- /dev/null
+++ b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/ClassName.java
@@ -0,0 +1,431 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2010 Oracle. 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:
+ * Oracle - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.jpt.common.utility.internal;
+
+/**
+ * Convenience methods related to Java class names as returned by
+ * {@link java.lang.Class#getName()}.
+ */
+public final class ClassName {
+
+ public static final String VOID_CLASS_NAME = ReflectionTools.VOID_CLASS.getName();
+ public static final String VOID_WRAPPER_CLASS_NAME = ReflectionTools.VOID_WRAPPER_CLASS.getName();
+
+ public static final char REFERENCE_CLASS_CODE = 'L';
+ public static final char REFERENCE_CLASS_NAME_DELIMITER = ';';
+
+ /**
+ * Return whether the specified class is an array type.
+ * @see java.lang.Class#getName()
+ */
+ public static boolean isArray(String className) {
+ return className.charAt(0) == '[';
+ }
+
+ /**
+ * Return the "element type" of the specified class.
+ * The element type is the base type held by an array type.
+ * Non-array types simply return themselves.
+ * @see java.lang.Class#getName()
+ */
+ public static String getElementTypeName(String className) {
+ int depth = getArrayDepth(className);
+ if (depth == 0) {
+ // the name is in the form: "java.lang.Object" or "int"
+ return className;
+ }
+ return getElementTypeName_(className, depth);
+ }
+
+ /**
+ * pre-condition: array depth is not zero
+ */
+ private static String getElementTypeName_(String className, int arrayDepth) {
+ int last = className.length() - 1;
+ if (className.charAt(arrayDepth) == REFERENCE_CLASS_CODE) {
+ // the name is in the form: "[[[Ljava.lang.Object;"
+ return className.substring(arrayDepth + 1, last); // drop the trailing ';'
+ }
+ // the name is in the form: "[[[I"
+ return forCode(className.charAt(last));
+ }
+
+ /**
+ * Return the "array depth" of the specified class.
+ * The depth is the number of dimensions for an array type.
+ * Non-array types have a depth of zero.
+ * @see java.lang.Class#getName()
+ */
+ public static int getArrayDepth(String className) {
+ int depth = 0;
+ while (className.charAt(depth) == '[') {
+ depth++;
+ }
+ return depth;
+ }
+
+ /**
+ * Return the specified class's component type.
+ * Return <code>null</code> if the specified class is not an array type.
+ * @see java.lang.Class#getName()
+ */
+ public static String getComponentTypeName(String className) {
+ switch (getArrayDepth(className)) {
+ case 0:
+ return null;
+ case 1:
+ return getElementTypeName_(className, 1);
+ default:
+ return className.substring(1);
+ }
+ }
+
+ /**
+ * Return the specified class's simple name.
+ * Return an empty string if the specified class is anonymous.
+ * <p>
+ * The simple name of an array type is the simple name of the
+ * component type with <code>"[]"</code> appended. In particular,
+ * the simple name of an array type whose component type is
+ * anonymous is simply <code>"[]"</code>.
+ * @see java.lang.Class#getSimpleName()
+ */
+ public static String getSimpleName(String className) {
+ return isArray(className) ?
+ getSimpleName(getComponentTypeName(className)) + "[]" : //$NON-NLS-1$
+ getSimpleName_(className);
+ }
+
+ /**
+ * pre-condition: specified class is not an array type
+ */
+ private static String getSimpleName_(String className) {
+ int index = className.lastIndexOf('$');
+ if (index == -1) { // "top-level" class - strip package name
+ return className.substring(className.lastIndexOf('.') + 1);
+ }
+
+ int len = className.length();
+ for (int i = ++index; i < len; i++) {
+ if ( ! charIsAsciiDigit(className.charAt(i))) {
+ return className.substring(i); // "member" or "local" class
+ }
+ }
+ // all the characters past the '$' are ASCII digits ("anonymous" class)
+ return ""; //$NON-NLS-1$
+ }
+
+ /**
+ * Return the specified class's package name (e.g.
+ * <code>"java.lang.Object"</code> returns
+ * <code>"java.lang"</code>).
+ * Return an empty string if the specified class is:<ul>
+ * <li>in the "default" package
+ * <li>an array class
+ * <li>a primtive class
+ * </ul>
+ * @see java.lang.Class#getPackage()
+ * @see java.lang.Package#getName()
+ */
+ public static String getPackageName(String className) {
+ if (isArray(className)) {
+ return ""; //$NON-NLS-1$
+ }
+ int lastPeriod = className.lastIndexOf('.');
+ return (lastPeriod == -1) ? "" : className.substring(0, lastPeriod); //$NON-NLS-1$
+ }
+
+ /**
+ * Return whether the specified class is a "top-level" class,
+ * as opposed to a "member", "local", or "anonymous" class,
+ * using the standard JDK naming conventions (i.e. the class
+ * name does NOT contain a <code>'$'</code>).
+ * A "top-level" class can be either the "primary" (public) class defined
+ * in a file/compilation unit (i.e. the class with the same name as its
+ * file's simple base name) or a "non-primary" (package visible) class
+ * (i.e. the other top-level classes defined in a file).
+ * A "top-level" class can contain any of the other types of classes.
+ * @see java.lang.Class#getName()
+ */
+ public static boolean isTopLevel(String className) {
+ if (isArray(className)) {
+ return false;
+ }
+ return className.lastIndexOf('$') == -1;
+ }
+
+ /**
+ * Return whether the specified class is a "member" class,
+ * as opposed to a "top-level", "local", or "anonymous" class,
+ * using the standard JDK naming convention (i.e. the class
+ * name ends with a <code>'$'</code> followed by a legal class name; e.g.
+ * <code>"TopLevelClass$1LocalClass$MemberClass"</code>).
+ * A "member" class can be either "nested" (static) or "inner";
+ * but there is no way to determine which from the class's name.
+ * A "member" class can contain "local", "anonymous", or other
+ * "member" classes; and vice-versa.
+ * @see java.lang.Class#getName()
+ */
+ public static boolean isMember(String className) {
+ if (isArray(className)) {
+ return false;
+ }
+ int index = className.lastIndexOf('$');
+ if (index == -1) {
+ return false; // "top-level" class
+ }
+ // the character immediately after the dollar sign cannot be an ASCII digit
+ return ! charIsAsciiDigit(className.charAt(++index));
+ }
+
+ /**
+ * Return whether the specified class is a "local" class,
+ * as opposed to a "top-level", "member", or "anonymous" class,
+ * using the standard JDK naming convention (i.e. the class name
+ * ends with <code>"$nnnXXX"</code>,
+ * where the <code>'$'</code> is
+ * followed by a series of numeric digits which are followed by the
+ * local class name; e.g. <code>"TopLevelClass$1LocalClass"</code>).
+ * A "local" class can contain "member", "anonymous", or other
+ * "local" classes; and vice-versa.
+ * @see java.lang.Class#getName()
+ */
+ public static boolean isLocal(String className) {
+ if (isArray(className)) {
+ return false;
+ }
+ int index = className.lastIndexOf('$');
+ if (index == -1) {
+ return false; // "top-level" class
+ }
+ if ( ! charIsAsciiDigit(className.charAt(++index))) {
+ return false; // "member" class
+ }
+ int len = className.length();
+ for (int i = ++index; i < len; i++) {
+ if ( ! charIsAsciiDigit(className.charAt(i))) {
+ return true;
+ }
+ }
+ // all the characters past the '$' are ASCII digits ("anonymous" class)
+ return false;
+ }
+
+ /**
+ * Return whether the specified class is an "anonymous" class,
+ * as opposed to a "top-level", "member", or "local" class,
+ * using the standard JDK naming convention (i.e. the class
+ * name ends with <code>"$nnn"</code> where all the characters past the
+ * last <code>'$'</code> are ASCII numeric digits;
+ * e.g. <code>"TopLevelClass$1"</code>).
+ * An "anonymous" class can contain "member", "local", or other
+ * "anonymous" classes; and vice-versa.
+ * @see java.lang.Class#getName()
+ */
+ public static boolean isAnonymous(String className) {
+ if (isArray(className)) {
+ return false;
+ }
+ int index = className.lastIndexOf('$');
+ if (index == -1) {
+ return false; // "top-level" class
+ }
+ if ( ! charIsAsciiDigit(className.charAt(++index))) {
+ return false; // "member" class
+ }
+ int len = className.length();
+ for (int i = ++index; i < len; i++) {
+ if ( ! charIsAsciiDigit(className.charAt(i))) {
+ return false; // "local" class
+ }
+ }
+ // all the characters past the '$' are ASCII digits ("anonymous" class)
+ return true;
+ }
+
+ /**
+ * {@link Character#isDigit(char)} returns <code>true</code> for some non-ASCII
+ * digits. This method does not.
+ */
+ private static boolean charIsAsciiDigit(char c) {
+ return ('0' <= c) && (c <= '9');
+ }
+
+ /**
+ * Return whether the specified class is a "reference"
+ * class (i.e. neither <code>void</code> nor one of the primitive variable classes,
+ * <code>boolean</code>, <code>int</code>, <code>float</code>, etc.).
+ * <p>
+ * <strong>NB:</strong> <code>void.class.isPrimitive() == true</code>
+ */
+ public static boolean isReference(String className) {
+ return ! isPrimitive(className);
+ }
+
+ /**
+ * Return whether the specified class is a primitive
+ * class (i.e. <code>void</code> or one of the primitive variable classes,
+ * <code>boolean</code>, <code>int</code>, <code>float</code>, etc.).
+ * <p>
+ * <strong>NB:</strong> <code>void.class.isPrimitive() == true</code>
+ */
+ public static boolean isPrimitive(String className) {
+ if (isArray(className) || (className.length() > ReflectionTools.MAX_PRIMITIVE_CLASS_NAME_LENGTH)) {
+ return false; // performance tweak
+ }
+ for (ReflectionTools.Primitive primitive : ReflectionTools.PRIMITIVES) {
+ if (className.equals(primitive.javaClass.getName())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Return whether the specified class is a primitive wrapper
+ * class (i.e. <code>java.lang.Void</code> or one of the primitive
+ * variable wrapper classes, <code>java.lang.Boolean</code>,
+ * <code>java.lang.Integer</code>, <code>java.lang.Float</code>, etc.).
+ * <p>
+ * <strong>NB:</strong> <code>void.class.isPrimitive() == true</code>
+ */
+ public static boolean isPrimitiveWrapper(String className) {
+ if (isArray(className) || (className.length() > ReflectionTools.MAX_PRIMITIVE_WRAPPER_CLASS_NAME_LENGTH)) {
+ return false; // performance tweak
+ }
+ for (ReflectionTools.Primitive primitive : ReflectionTools.PRIMITIVES) {
+ if (className.equals(primitive.wrapperClass.getName())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Return whether the specified class is a "variable" primitive
+ * class (i.e. <code>boolean</code>, <code>int</code>, <code>float</code>, etc.,
+ * but not <code>void</code>).
+ * <p>
+ * <strong>NB:</strong> <code>void.class.isPrimitive() == true</code>
+ */
+ public static boolean isVariablePrimitive(String className) {
+ return isPrimitive(className)
+ && ( ! className.equals(VOID_CLASS_NAME));
+ }
+
+ /**
+ * Return whether the specified class is a "variable" primitive wrapper
+ * class (i.e. <code>java.lang.Boolean</code>,
+ * <code>java.lang.Integer</code>, <code>java.lang.Float</code>, etc.,
+ * but not <code>java.lang.Void</code>).
+ * <p>
+ * <strong>NB:</strong> <code>void.class.isPrimitive() == true</code>
+ */
+ public static boolean isVariablePrimitiveWrapper(String className) {
+ return isPrimitiveWrapper(className)
+ && ( ! className.equals(VOID_WRAPPER_CLASS_NAME));
+ }
+
+ /**
+ * Return the name of the primitive wrapper class corresponding to the specified
+ * primitive class. Return <code>null</code> if the specified class is not a primitive.
+ */
+ public static String getWrapperClassName(String primitiveClassName) {
+ for (ReflectionTools.Primitive primitive : ReflectionTools.PRIMITIVES) {
+ if (primitive.javaClass.getName().equals(primitiveClassName)) {
+ return primitive.wrapperClass.getName();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Return the name of the primitive class corresponding to the specified
+ * primitive wrapper class. Return <code>null</code> if the specified class
+ * is not a primitive wrapper.
+ */
+ public static String getPrimitiveClassName(String primitiveWrapperClassName) {
+ for (ReflectionTools.Primitive primitive : ReflectionTools.PRIMITIVES) {
+ if (primitive.wrapperClass.getName().equals(primitiveWrapperClassName)) {
+ return primitive.javaClass.getName();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Return whether the two class names are equivalent, given autoboxing.
+ * (e.g. "java.lang.Integer" and "int" should be equivalent)
+ */
+ public static boolean areAutoboxEquivalents(String className1, String className2) {
+ return Tools.valuesAreEqual(className1, className2)
+ || Tools.valuesAreEqual(getPrimitiveClassName(className1), className2)
+ || Tools.valuesAreEqual(getWrapperClassName(className1), className2);
+ }
+
+
+ // ********** primitive codes **********
+
+ /**
+ * Return the primitive class name for the specified primitive class code.
+ * Return <code>null</code> if the specified code
+ * is not a primitive class code.
+ * @see java.lang.Class#getName()
+ */
+ public static String forCode(int classCode) {
+ return forCode((char) classCode);
+ }
+
+ /**
+ * Return the primitive class name for the specified primitive class code.
+ * Return <code>null</code> if the specified code
+ * is not a primitive class code.
+ * @see java.lang.Class#getName()
+ */
+ public static String forCode(char classCode) {
+ Class<?> primitiveClass = ReflectionTools.getClassForCode(classCode);
+ return (primitiveClass == null) ? null : primitiveClass.getName();
+ }
+
+ /**
+ * Return the class code for the specified primitive class.
+ * Return <code>0</code> if the specified class
+ * is not a primitive class.
+ * @see java.lang.Class#getName()
+ */
+ public static char getCodeForClassName(String primitiveClassName) {
+ if (( ! isArray(primitiveClassName)) && (primitiveClassName.length() <= ReflectionTools.MAX_PRIMITIVE_CLASS_NAME_LENGTH)) {
+ for (ReflectionTools.Primitive primitive : ReflectionTools.PRIMITIVES) {
+ if (primitive.javaClass.getName().equals(primitiveClassName)) {
+ return primitive.code;
+ }
+ }
+ }
+ return 0;
+ }
+
+ static void append(String className, StringBuilder sb) {
+ sb.append(REFERENCE_CLASS_CODE);
+ sb.append(className);
+ sb.append(REFERENCE_CLASS_NAME_DELIMITER);
+ }
+
+
+ // ********** suppressed constructor **********
+
+ /**
+ * Suppress default constructor, ensuring non-instantiability.
+ */
+ private ClassName() {
+ super();
+ throw new UnsupportedOperationException();
+ }
+
+}
diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/Classpath.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/Classpath.java
new file mode 100644
index 0000000000..dcf03bd2cf
--- /dev/null
+++ b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/Classpath.java
@@ -0,0 +1,939 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2010 Oracle. 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:
+ * Oracle - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.jpt.common.utility.internal;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.io.IOException;
+import java.io.Serializable;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+import org.eclipse.jpt.common.utility.Filter;
+import org.eclipse.jpt.common.utility.internal.iterables.ArrayIterable;
+import org.eclipse.jpt.common.utility.internal.iterators.ArrayIterator;
+import org.eclipse.jpt.common.utility.internal.iterators.CompositeIterator;
+import org.eclipse.jpt.common.utility.internal.iterators.EmptyIterator;
+import org.eclipse.jpt.common.utility.internal.iterators.FilteringIterator;
+import org.eclipse.jpt.common.utility.internal.iterators.TransformationIterator;
+
+/**
+ * <code>Classpath</code> models a Java classpath, which consists of a list of
+ * {@link Entry}s, each of which contain Java classes. The classpath can return
+ * the names of classes found in it etc. There are a number of static
+ * convenience methods that can be use to construct <code>Classpath</code>s
+ * corresponding to the Java classpath etc.
+ */
+public class Classpath
+ implements Serializable
+{
+ /** The entries in the classpath */
+ private final Entry[] entries;
+
+ private static final long serialVersionUID = 1L;
+
+
+ // ********** static methods **********
+
+ // ***** factory methods for "standard" classpaths *****
+
+ /**
+ * Return the Java "boot" classpath. This includes <code>rt.jar</code>.
+ */
+ public static Classpath bootClasspath() {
+ return new Classpath(System.getProperty("sun.boot.class.path")); //$NON-NLS-1$
+ }
+
+ /**
+ * Return a "virtual classpath" that contains all the jars
+ * that would be used by the Java Extension Mechanism.
+ */
+ public static Classpath javaExtensionClasspath() {
+ File[] dirs = javaExtensionDirectories();
+ List<String> jarFileNames = new ArrayList<String>();
+ for (File dir : dirs) {
+ if (dir.isDirectory()) {
+ addJarFileNamesTo(dir, jarFileNames);
+ }
+ }
+ return new Classpath(jarFileNames);
+ }
+
+ /**
+ * Return the Java "system" classpath.
+ */
+ public static Classpath javaClasspath() {
+ return new Classpath(System.getProperty("java.class.path")); //$NON-NLS-1$
+ }
+
+ /**
+ * Return the unretouched "complete" classpath.
+ * This includes the boot classpath, the Java Extension
+ * Mechanism classpath, and the normal "system" classpath.
+ */
+ public static Classpath completeClasspath() {
+ return new Classpath(new Classpath[] {
+ bootClasspath(),
+ javaExtensionClasspath(),
+ javaClasspath()
+ });
+ }
+
+ /**
+ * Return a classpath that contains the location of the specified class.
+ */
+ public static Classpath classpathFor(Class<?> javaClass) {
+ return new Classpath(locationFor(javaClass));
+ }
+
+
+ // ***** file => class *****
+
+ /**
+ * Convert a relative file name to a class name; this will work for
+ * any file that has a single extension beyond the base
+ * class name:<ul>
+ * <li><code>"java/lang/String.class"</code> is converted to <code>"java.lang.String"</code>
+ * <li><code>"java/lang/String.java"</code> is converted to <code>"java.lang.String"</code>
+ * </ul>
+ */
+ public static String convertToClassName(String classFileName) {
+ String className = FileTools.stripExtension(classFileName);
+ // do this for archive entry names
+ className = className.replace('/', '.');
+ // do this for O/S-specific file names
+ if (File.separatorChar != '/') {
+ className = className.replace(File.separatorChar, '.');
+ }
+ return className;
+ }
+
+ /**
+ * Convert a file to a class name;
+ * e.g. <code>File(java/lang/String.class)</code> is converted to
+ * <code>"java.lang.String"</code>.
+ */
+ public static String convertToClassName(File classFile) {
+ return convertToClassName(classFile.getPath());
+ }
+
+ /**
+ * Convert a relative file name to a class;
+ * e.g. <code>"java/lang/String.class"</code> is converted to
+ * <code>java.lang.String.class</code>.
+ */
+ public static Class<?> convertToClass(String classFileName) throws ClassNotFoundException {
+ return Class.forName(convertToClassName(classFileName));
+ }
+
+ /**
+ * Convert a relative file to a class;
+ * e.g. <code>File(java/lang/String.class)</code> is converted to
+ * <code>java.lang.String.class</code>.
+ */
+ public static Class<?> convertToClass(File classFile) throws ClassNotFoundException {
+ return convertToClass(classFile.getPath());
+ }
+
+
+ // ***** class => JAR entry *****
+
+ /**
+ * Convert a class name to an archive entry name base;
+ * e.g. <code>"java.lang.String"</code> is converted to
+ * <code>"java/lang/String"</code>.
+ */
+ public static String convertToArchiveEntryNameBase(String className) {
+ return className.replace('.', '/');
+ }
+
+ /**
+ * Convert a class to an archive entry name base;
+ * e.g. <code>java.lang.String.class</code> is converted to
+ * <code>"java/lang/String"</code>.
+ */
+ public static String convertToArchiveEntryNameBase(Class<?> javaClass) {
+ return convertToArchiveEntryNameBase(javaClass.getName());
+ }
+
+ /**
+ * Convert a class name to an archive class file entry name;
+ * e.g. <code>"java.lang.String"</code> is converted to
+ * <code>"java/lang/String.class"</code>.
+ */
+ public static String convertToArchiveClassFileEntryName(String className) {
+ return convertToArchiveEntryNameBase(className) + ".class"; //$NON-NLS-1$
+ }
+
+ /**
+ * Convert a class to an archive class file entry name;
+ * e.g. <code>java.lang.String.class</code> is converted to
+ * <code>"java/lang/String.class"</code>.
+ */
+ public static String convertToArchiveClassFileEntryName(Class<?> javaClass) {
+ return convertToArchiveClassFileEntryName(javaClass.getName());
+ }
+
+
+ // ***** class => file (.class or .java) *****
+
+ /**
+ * Convert a class name to a file name base for the current O/S;
+ * e.g. <code>"java.lang.String"</code> is converted to
+ * <code>"java/lang/String"</code> on Unix and
+ * <code>"java\\lang\\String"</code> on Windows.
+ */
+ public static String convertToFileNameBase(String className) {
+ return className.replace('.', File.separatorChar);
+ }
+
+ /**
+ * Convert a class to a file name base for the current O/S;
+ * e.g. <code>java.lang.String.class</code> is converted to
+ * <code>"java/lang/String"</code> on Unix and
+ * <code>"java\\lang\\String"</code> on Windows.
+ */
+ public static String convertToFileNameBase(Class<?> javaClass) {
+ return convertToFileNameBase(javaClass.getName());
+ }
+
+ /**
+ * Convert a class name to a class file name for the current O/S;
+ * e.g. <code>"java.lang.String"</code> is converted to
+ * <code>"java/lang/String.class"</code> on Unix and
+ * <code>"java\\lang\\String.class"</code> on Windows.
+ */
+ public static String convertToClassFileName(String className) {
+ return convertToFileNameBase(className) + ".class"; //$NON-NLS-1$
+ }
+
+ /**
+ * Convert a class to a class file name for the current O/S;
+ * e.g. <code>java.lang.String.class</code> is converted to
+ * <code>"java/lang/String.class"</code> on Unix and
+ * <code>"java\\lang\\String.class"</code> on Windows.
+ */
+ public static String convertToClassFileName(Class<?> javaClass) {
+ return convertToClassFileName(javaClass.getName());
+ }
+
+ /**
+ * Convert a class name to a class file for the current O/S;
+ * e.g. <code>"java.lang.String"</code> is converted to
+ * <code>File(java/lang/String.class)</code>.
+ */
+ public static File convertToClassFile(String className) {
+ return new File(convertToClassFileName(className));
+ }
+
+ /**
+ * Convert a class to a class file for the current O/S;
+ * e.g. <code>java.lang.String.class</code> is converted to
+ * <code>File(java/lang/String.class)</code>.
+ */
+ public static File convertToClassFile(Class<?> javaClass) {
+ return convertToClassFile(javaClass.getName());
+ }
+
+ /**
+ * Convert a class name to a java file name for the current O/S;
+ * e.g. <code>"java.lang.String"</code> is converted to
+ * <code>"java/lang/String.java"</code> on Unixl and
+ * <code>"java\\lang\\String.java"</code> on Windows.
+ */
+ public static String convertToJavaFileName(String className) {
+ return convertToFileNameBase(className) + ".java"; //$NON-NLS-1$
+ }
+
+ /**
+ * Convert a class to a java file name for the current O/S;
+ * e.g. <code>java.lang.String.class</code> is converted to
+ * <code>"java/lang/String.java"</code> on Unix and
+ * <code>"java\\lang\\String.java"</code> on Windows.
+ */
+ public static String convertToJavaFileName(Class<?> javaClass) {
+ return convertToJavaFileName(javaClass.getName());
+ }
+
+ /**
+ * Convert a class name to a java file for the current O/S;
+ * e.g. <code>"java.lang.String"</code> is converted to
+ * <code>File(java/lang/String.java)</code>.
+ */
+ public static File convertToJavaFile(String className) {
+ return new File(convertToJavaFileName(className));
+ }
+
+ /**
+ * Convert a class to a java file for the current O/S;
+ * e.g. <code>java.lang.String.class</code> is converted to
+ * <code>File(java/lang/String.java)</code>.
+ */
+ public static File convertToJavaFile(Class<?> javaClass) {
+ return convertToJavaFile(javaClass.getName());
+ }
+
+
+ // ***** class => resource *****
+
+ /**
+ * Convert a class to a resource name;
+ * e.g. <code>java.lang.String.class</code> is converted to
+ * <code>"/java/lang/String.class"</code>.
+ */
+ public static String convertToResourceName(Class<?> javaClass) {
+ return '/' + convertToArchiveClassFileEntryName(javaClass);
+ }
+
+ /**
+ * Convert a class to a resource;
+ * e.g. <code>java.lang.String.class</code> is converted to
+ * <code>URL(jar:file:/C:/jdk/1.4.2_04/jre/lib/rt.jar!/java/lang/String.class)</code>.
+ */
+ public static URL convertToResource(Class<?> javaClass) {
+ return javaClass.getResource(convertToResourceName(javaClass));
+ }
+
+
+ // ***** utilities *****
+
+ /**
+ * Return whether the specified file is an archive file;
+ * i.e. its name ends with <code>".zip"</code> or <code>".jar"</code>.
+ */
+ public static boolean fileNameIsArchive(String fileName) {
+ String ext = FileTools.extension(fileName).toLowerCase();
+ return ext.equals(".jar") || ext.equals(".zip"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ /**
+ * Return whether the specified file is an archive file;
+ * i.e. its name ends with <code>".zip"</code> or <code>".jar"</code>.
+ */
+ public static boolean fileIsArchive(File file) {
+ return fileNameIsArchive(file.getName());
+ }
+
+ /**
+ * Return what should be the fully-qualified file name
+ * for the JRE runtime JAR;
+ * e.g. <code>"C:\jdk1.4.2_04\jre\lib\rt.jar"</code>.
+ */
+ public static String rtJarName() {
+ return locationFor(java.lang.Object.class);
+ }
+
+ /**
+ * Return the location from where the specified class was loaded.
+ */
+ public static String locationFor(Class<?> javaClass) {
+ URL url = convertToResource(javaClass);
+ String path;
+ try {
+ path = FileTools.buildFile(url).getPath();
+ } catch (URISyntaxException ex) {
+ throw new RuntimeException(ex);
+ }
+ String protocol = url.getProtocol().toLowerCase();
+ if (protocol.equals("jar")) { //$NON-NLS-1$
+ // if the class is in a JAR, the URL will look something like this:
+ // jar:file:/C:/jdk/1.4.2_04/jre/lib/rt.jar!/java/lang/String.class
+ return path.substring(0, path.indexOf('!'));
+ } else if (protocol.equals("file")) { //$NON-NLS-1$
+ // if the class is in a directory, the URL will look something like this:
+ // file:/C:/dev/main/mwdev/class/org/eclipse/dali/utility/Classpath.class
+ return path.substring(0, path.length() - convertToClassFileName(javaClass).length() - 1);
+ } else if (protocol.equals("bundleresource")) { //$NON-NLS-1$
+ // if the class is in a bundle resource (Eclipse?), the URL will look something like this:
+ // bundleresource://43/org/eclipse/dali/utility/Classpath.class
+ return path.substring(0, path.length() - convertToClassFileName(javaClass).length() - 1);
+ }
+
+ throw new IllegalStateException(url.toString());
+ }
+
+ /**
+ * Return the directories used by the Java Extension Mechanism.
+ */
+ public static File[] javaExtensionDirectories() {
+ return convertToFiles(javaExtensionDirectoryNames());
+ }
+
+ /**
+ * Return the directory names used by the Java Extension Mechanism.
+ */
+ public static String[] javaExtensionDirectoryNames() {
+ return System.getProperty("java.ext.dirs").split(File.pathSeparator); //$NON-NLS-1$
+ }
+
+
+ // ***** internal *****
+
+ private static File[] convertToFiles(String[] fileNames) {
+ File[] files = new File[fileNames.length];
+ for (int i = fileNames.length; i-- > 0; ) {
+ files[i] = new File(fileNames[i]);
+ }
+ return files;
+ }
+
+ private static void addJarFileNamesTo(File dir, List<String> jarFileNames) {
+ File[] jarFiles = jarFilesIn(dir);
+ for (File jarFile : jarFiles) {
+ jarFileNames.add(FileTools.canonicalFile(jarFile).getPath());
+ }
+ }
+
+ private static File[] jarFilesIn(File directory) {
+ return directory.listFiles(jarFileFilter());
+ }
+
+ private static FileFilter jarFileFilter() {
+ return new FileFilter() {
+ public boolean accept(File file) {
+ return FileTools.extension(file.getName()).toLowerCase().equals(".jar"); //$NON-NLS-1$
+ }
+ };
+ }
+
+
+ // ********** constructors **********
+
+ /**
+ * Construct a classpath with the specified entries.
+ */
+ private Classpath(Entry[] entries) {
+ super();
+ this.entries = entries;
+ }
+
+ /**
+ * Construct a classpath with the specified entries.
+ */
+ public Classpath(String... fileNames) {
+ this(buildEntries(fileNames));
+ }
+
+ /**
+ * Skip empty file names because they will end up expanding to the current
+ * working directory, which is not what we want. Empty file names actually
+ * occur with some frequency; such as when the classpath has been built up
+ * dynamically with too many separators. For example:<pre>
+ * "C:\dev\foo.jar;;C:\dev\bar.jar"
+ * </pre>will be parsed into three file names:<pre>
+ * { "C:\dev\foo.jar", "", "C:\dev\bar.jar" }
+ * </pre>
+ */
+ private static Entry[] buildEntries(String[] fileNames) {
+ List<Entry> entries = new ArrayList<Entry>();
+ for (String fileName : fileNames) {
+ if ((fileName != null) && (fileName.length() != 0)) {
+ entries.add(new Entry(fileName));
+ }
+ }
+ return entries.toArray(new Entry[entries.size()]);
+ }
+
+ /**
+ * Construct a classpath with the specified path.
+ */
+ public Classpath(String path) {
+ this(path.split(File.pathSeparator));
+ }
+
+ /**
+ * Construct a classpath with the specified entries.
+ */
+ public Classpath(Iterable<String> fileNames) {
+ this(ArrayTools.array(fileNames, StringTools.EMPTY_STRING_ARRAY));
+ }
+
+ /**
+ * Consolidate the specified classpaths into a single classpath.
+ */
+ public Classpath(Classpath... classpaths) {
+ this(consolidateEntries(classpaths));
+ }
+
+ private static Entry[] consolidateEntries(Classpath[] classpaths) {
+ List<Entry> entries = new ArrayList<Entry>();
+ for (Classpath classpath : classpaths) {
+ CollectionTools.addAll(entries, classpath.getEntries());
+ }
+ return entries.toArray(new Entry[entries.size()]);
+ }
+
+
+ // ********** public API **********
+
+ /**
+ * Return the classpath's entries.
+ */
+ public Iterable<Entry> getEntries() {
+ return new ArrayIterable<Entry>(this.entries);
+ }
+
+ /**
+ * Return the classpath's path.
+ */
+ public String getPath() {
+ int max = this.entries.length - 1;
+ if (max == -1) {
+ return ""; //$NON-NLS-1$
+ }
+ StringBuilder sb = new StringBuilder(2000);
+ // stop one short of the end of the array
+ for (int i = 0; i < max; i++) {
+ sb.append(this.entries[i].getFileName());
+ sb.append(File.pathSeparatorChar);
+ }
+ sb.append(this.entries[max].getFileName());
+ return sb.toString();
+ }
+
+ /**
+ * Search the classpath for the specified (unqualified) file
+ * and return its entry. Return null if an entry is not found.
+ * For example, you could use this method to find the entry
+ * for <code>"rt.jar"</code> or <code>"toplink.jar"</code>.
+ */
+ public Entry getEntryForFileNamed(String shortFileName) {
+ for (Entry entry : this.entries) {
+ if (entry.getFile().getName().equals(shortFileName)) {
+ return entry;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Return the first entry file in the classpath
+ * that contains the specified class.
+ * Return null if an entry is not found.
+ */
+ public Entry getEntryForClassNamed(String className) {
+ String relativeClassFileName = convertToClassFileName(className);
+ String archiveEntryName = convertToArchiveClassFileEntryName(className);
+ for (Entry entry : this.entries) {
+ if (entry.contains(relativeClassFileName, archiveEntryName)) {
+ return entry;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Return the names of all the classes discovered on the classpath,
+ * with duplicates removed.
+ * @see #classNames()
+ */
+ public Iterable<String> getClassNames() {
+ return this.getClassNames(Filter.Null.<String>instance());
+ }
+
+ /**
+ * Return the names of all the classes discovered on the classpath
+ * and accepted by the specified filter, with duplicates removed.
+ * @see #classNames(Filter)
+ */
+ public Iterable<String> getClassNames(Filter<String> filter) {
+ Collection<String> classNames = new HashSet<String>(10000);
+ this.addClassNamesTo(classNames, filter);
+ return classNames;
+ }
+
+ /**
+ * Add the names of all the classes discovered on the classpath
+ * to the specified collection.
+ */
+ public void addClassNamesTo(Collection<String> classNames) {
+ this.addClassNamesTo(classNames, Filter.Null.<String>instance());
+ }
+
+ /**
+ * Add the names of all the classes discovered on the classpath
+ * and accepted by the specified filter to the specified collection.
+ */
+ public void addClassNamesTo(Collection<String> classNames, Filter<String> filter) {
+ for (Entry entry : this.entries) {
+ entry.addClassNamesTo(classNames, filter);
+ }
+ }
+
+ /**
+ * Return the names of all the classes discovered on the classpath.
+ * Just a bit more performant than {@link #getClassNames()}.
+ */
+ public Iterator<String> classNames() {
+ return this.classNames(Filter.Null.<String>instance());
+ }
+
+ /**
+ * Return the names of all the classes discovered on the classpath
+ * that are accepted by the specified filter.
+ * Just a bit more performant than {@link #getClassNames(Filter)}.
+ */
+ public Iterator<String> classNames(Filter<String> filter) {
+ return new CompositeIterator<String>(this.entryClassNamesIterators(filter));
+ }
+
+ private Iterator<Iterator<String>> entryClassNamesIterators(final Filter<String> filter) {
+ return new TransformationIterator<Entry, Iterator<String>>(new ArrayIterator<Entry>(this.entries)) {
+ @Override
+ protected Iterator<String> transform(Entry entry) {
+ return entry.classNames(filter);
+ }
+ };
+ }
+
+ /**
+ * Return a "compressed" version of the classpath with its
+ * duplicate entries eliminated.
+ */
+ public Classpath compressed() {
+ return new Classpath(ArrayTools.removeDuplicateElements(this.entries));
+ }
+
+ /**
+ * Convert the classpath to an array of URLs
+ * (that can be used to instantiate a {@link java.net.URLClassLoader}).
+ */
+ public Iterable<URL> getURLs() {
+ int len = this.entries.length;
+ URL[] urls = new URL[len];
+ for (int i = 0; i < len; i++) {
+ urls[i] = this.entries[i].getURL();
+ }
+ return new ArrayIterable<URL>(urls);
+ }
+
+ @Override
+ public String toString() {
+ return StringTools.buildToStringFor(this, this.getPath());
+ }
+
+
+ // ********** inner class **********
+
+ /**
+ * <code>Entry</code> models a Java classpath entry, which can be either a
+ * directory containing <code>.class</code> files or a JAR file (or,
+ * similarly, a <code>.zip</code> file). The entry can return the names of
+ * classes found in it etc.
+ */
+ public static class Entry implements Serializable {
+ private final String fileName;
+ private final File file;
+ private final File canonicalFile;
+
+ private static final long serialVersionUID = 1L;
+
+ Entry(String fileName) {
+ super();
+ if ((fileName == null) || (fileName.length() == 0)) {
+ throw new IllegalArgumentException("'fileName' must be non-empty"); //$NON-NLS-1$
+ }
+ this.fileName = fileName;
+ this.file = new File(fileName);
+ this.canonicalFile = FileTools.canonicalFile(this.file);
+ }
+
+ public String getFileName() {
+ return this.fileName;
+ }
+
+ public File getFile() {
+ return this.file;
+ }
+
+ public File getCanonicalFile() {
+ return this.canonicalFile;
+ }
+
+ public String getCanonicalFileName() {
+ return this.canonicalFile.getAbsolutePath();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if ( ! (o instanceof Entry)) {
+ return false;
+ }
+ return ((Entry) o).canonicalFile.equals(this.canonicalFile);
+ }
+
+ @Override
+ public int hashCode() {
+ return this.canonicalFile.hashCode();
+ }
+
+ /**
+ * Return the entry's "canonical" URL.
+ */
+ public URL getURL() {
+ try {
+ return this.canonicalFile.toURI().toURL();
+ } catch (IOException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ /**
+ * Return whether the entry contains the specified class.
+ */
+ public boolean contains(Class<?> javaClass) {
+ return this.contains(javaClass.getName());
+ }
+
+ /**
+ * Return whether the entry contains the specified class.
+ */
+ public boolean contains(String className) {
+ return this.contains(convertToClassFileName(className), convertToArchiveClassFileEntryName(className));
+ }
+
+ /**
+ * Return whether the entry contains either the specified relative
+ * class file or the specified archive entry.
+ * Not the prettiest signature, but it's internal....
+ */
+ boolean contains(String relativeClassFileName, String archiveEntryName) {
+ if ( ! this.canonicalFile.exists()) {
+ return false;
+ }
+ if (this.canonicalFile.isDirectory() && (new File(this.canonicalFile, relativeClassFileName)).exists()) {
+ return true;
+ }
+ return (fileIsArchive(this.canonicalFile) && this.archiveContainsEntry(archiveEntryName));
+ }
+
+ /**
+ * Return whether the entry's archive contains the specified entry.
+ */
+ private boolean archiveContainsEntry(String zipEntryName) {
+ ZipFile zipFile = null;
+ ZipEntry zipEntry = null;
+ try {
+ zipFile = new ZipFile(this.canonicalFile);
+ zipEntry = zipFile.getEntry(zipEntryName);
+ } catch (IOException ex) {
+ // something is wrong, leave the entry null
+ } finally {
+ try {
+ if (zipFile != null) {
+ zipFile.close();
+ }
+ } catch (IOException ex) {
+ zipEntry = null; // something is wrong, clear out the entry
+ }
+ }
+ return zipEntry != null;
+ }
+
+ /**
+ * Return the names of all the classes discovered in the entry.
+ * @see #classNames()
+ */
+ public Iterable<String> getClassNames() {
+ return this.getClassNames(Filter.Null.<String>instance());
+ }
+
+ /**
+ * Return the names of all the classes discovered in the entry
+ * and accepted by the specified filter.
+ * @see #classNames(Filter)
+ */
+ public Iterable<String> getClassNames(Filter<String> filter) {
+ Collection<String> classNames = new ArrayList<String>(2000);
+ this.addClassNamesTo(classNames, filter);
+ return classNames;
+ }
+
+ /**
+ * Add the names of all the classes discovered in the entry
+ * to the specified collection.
+ */
+ public void addClassNamesTo(Collection<String> classNames) {
+ this.addClassNamesTo(classNames, Filter.Null.<String>instance());
+ }
+
+ /**
+ * Add the names of all the classes discovered in the entry
+ * and accepted by the specified filter to the specified collection.
+ */
+ public void addClassNamesTo(Collection<String> classNames, Filter<String> filter) {
+ if (this.canonicalFile.exists()) {
+ if (this.canonicalFile.isDirectory()) {
+ this.addClassNamesForDirectoryTo(classNames, filter);
+ } else if (fileIsArchive(this.canonicalFile)) {
+ this.addClassNamesForArchiveTo(classNames, filter);
+ }
+ }
+ }
+
+ /**
+ * Add the names of all the classes discovered
+ * under the entry's directory and accepted by
+ * the specified filter to the specified collection.
+ */
+ private void addClassNamesForDirectoryTo(Collection<String> classNames, Filter<String> filter) {
+ int start = this.canonicalFile.getAbsolutePath().length() + 1;
+ for (Iterator<File> stream = this.classFilesForDirectory(); stream.hasNext(); ) {
+ String className = convertToClassName(stream.next().getAbsolutePath().substring(start));
+ if (filter.accept(className)) {
+ classNames.add(className);
+ }
+ }
+ }
+
+ /**
+ * Return an iterator on all the class files discovered
+ * under the entry's directory.
+ */
+ private Iterator<File> classFilesForDirectory() {
+ return new FilteringIterator<File>(FileTools.filesInTree(this.canonicalFile)) {
+ @Override
+ protected boolean accept(File next) {
+ return Entry.this.fileNameMightBeForClassFile(next.getName());
+ }
+ };
+ }
+
+ /**
+ * Add the names of all the classes discovered
+ * in the entry's archive file and accepted by the
+ * specified filter to the specified collection.
+ */
+ private void addClassNamesForArchiveTo(Collection<String> classNames, Filter<String> filter) {
+ ZipFile zipFile = null;
+ try {
+ zipFile = new ZipFile(this.canonicalFile);
+ } catch (IOException ex) {
+ throw new RuntimeException(ex);
+ }
+ for (Enumeration<? extends ZipEntry> stream = zipFile.entries(); stream.hasMoreElements(); ) {
+ ZipEntry zipEntry = stream.nextElement();
+ String zipEntryName = zipEntry.getName();
+ if (this.fileNameMightBeForClassFile(zipEntryName)) {
+ String className = convertToClassName(zipEntryName);
+ if (filter.accept(className)) {
+ classNames.add(className);
+ }
+ }
+ }
+ try {
+ zipFile.close();
+ } catch (IOException ex) {
+ return;
+ }
+ }
+
+ /**
+ * Return whether the specified file might be a Java class file.
+ * The file name must at least end with <code>".class"</code> and contain no spaces.
+ * (Neither class names nor package names may contain spaces.)
+ * Whether it actually is a class file will need to be determined by
+ * a class loader.
+ */
+ boolean fileNameMightBeForClassFile(String name) {
+ return FileTools.extension(name).toLowerCase().equals(".class") //$NON-NLS-1$
+ && (name.indexOf(' ') == -1);
+ }
+
+ /**
+ * Return the names of all the classes discovered on the classpath.
+ * Just a bit more performant than {@link #getClassNames()}.
+ */
+ public Iterator<String> classNames() {
+ return this.classNames(Filter.Null.<String>instance());
+ }
+
+ /**
+ * Return the names of all the classes discovered on the classpath
+ * that are accepted by the specified filter.
+ * Just a bit more performant than {@link #getClassNames(Filter)}.
+ */
+ public Iterator<String> classNames(Filter<String> filter) {
+ if (this.canonicalFile.exists()) {
+ if (this.canonicalFile.isDirectory()) {
+ return this.classNamesForDirectory(filter);
+ }
+ if (fileIsArchive(this.canonicalFile)) {
+ return this.classNamesForArchive(filter);
+ }
+ }
+ return EmptyIterator.instance();
+ }
+
+ /**
+ * Return the names of all the classes discovered
+ * under the entry's directory and accepted by
+ * the specified filter.
+ */
+ private Iterator<String> classNamesForDirectory(Filter<String> filter) {
+ return new FilteringIterator<String>(this.classNamesForDirectory(), filter);
+ }
+
+ /**
+ * Transform the class files to class names.
+ */
+ private Iterator<String> classNamesForDirectory() {
+ final int start = this.canonicalFile.getAbsolutePath().length() + 1;
+ return new TransformationIterator<File, String>(this.classFilesForDirectory()) {
+ @Override
+ protected String transform(File f) {
+ return convertToClassName(f.getAbsolutePath().substring(start));
+ }
+ };
+ }
+
+ /**
+ * Return the names of all the classes discovered
+ * in the entry's archive file and accepted by the
+ * specified filter.
+ */
+ private Iterator<String> classNamesForArchive(Filter<String> filter) {
+ // we can't simply wrap iterators here because we need to close the archive file...
+ ZipFile zipFile = null;
+ try {
+ zipFile = new ZipFile(this.canonicalFile);
+ } catch (IOException ex) {
+ return EmptyIterator.instance();
+ }
+ Collection<String> classNames = new HashSet<String>(zipFile.size());
+ for (Enumeration<? extends ZipEntry> stream = zipFile.entries(); stream.hasMoreElements(); ) {
+ ZipEntry zipEntry = stream.nextElement();
+ String zipEntryName = zipEntry.getName();
+ if (this.fileNameMightBeForClassFile(zipEntryName)) {
+ String className = convertToClassName(zipEntryName);
+ if (filter.accept(className)) {
+ classNames.add(className);
+ }
+ }
+ }
+ try {
+ zipFile.close();
+ } catch (IOException ex) {
+ return EmptyIterator.instance();
+ }
+ return classNames.iterator();
+ }
+
+ }
+
+}
diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/CollectionTools.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/CollectionTools.java
new file mode 100644
index 0000000000..aca8d2c354
--- /dev/null
+++ b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/CollectionTools.java
@@ -0,0 +1,1957 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2010 Oracle. 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:
+ * Oracle - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.jpt.common.utility.internal;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Random;
+import java.util.RandomAccess;
+import java.util.TreeSet;
+import java.util.Vector;
+
+import org.eclipse.jpt.common.utility.internal.iterables.ArrayIterable;
+import org.eclipse.jpt.common.utility.internal.iterators.ArrayIterator;
+import org.eclipse.jpt.common.utility.internal.iterators.ArrayListIterator;
+import org.eclipse.jpt.common.utility.internal.iterators.SingleElementIterator;
+import org.eclipse.jpt.common.utility.internal.iterators.SingleElementListIterator;
+import org.eclipse.jpt.common.utility.internal.iterators.SuperIteratorWrapper;
+
+/**
+ * {@link Collection}-related utility methods.
+ */
+public final class CollectionTools {
+
+ // ********** add all **********
+
+ /**
+ * Add all the elements returned by the specified iterable
+ * to the specified collection.
+ * Return whether the collection changed as a result.
+ * <p>
+ * <code>Collection.addAll(Iterable iterable)</code>
+ */
+ public static <E> boolean addAll(Collection<? super E> collection, Iterable<? extends E> iterable) {
+ return addAll(collection, iterable.iterator());
+ }
+
+ /**
+ * Add all the elements returned by the specified iterable
+ * to the specified collection.
+ * Return whether the collection changed as a result.
+ * <p>
+ * <code>Collection.addAll(Iterable iterable)</code>
+ */
+ public static <E> boolean addAll(Collection<? super E> collection, Iterable<? extends E> iterable, int size) {
+ return addAll(collection, iterable.iterator(), size);
+ }
+
+ /**
+ * Add all the elements returned by the specified iterator
+ * to the specified collection.
+ * Return whether the collection changed as a result.
+ * <p>
+ * <code>Collection.addAll(Iterator iterator)</code>
+ */
+ public static <E> boolean addAll(Collection<? super E> collection, Iterator<? extends E> iterator) {
+ return iterator.hasNext() ? addAll_(collection, iterator) : false;
+ }
+
+ /**
+ * assume the iterator is not empty
+ */
+ private static <E> boolean addAll_(Collection<? super E> collection, Iterator<? extends E> iterator) {
+ boolean modified = false;
+ while (iterator.hasNext()) {
+ modified |= collection.add(iterator.next());
+ }
+ return modified;
+ }
+
+ /**
+ * Add all the elements returned by the specified iterator
+ * to the specified collection.
+ * Return whether the collection changed as a result.
+ * <p>
+ * <code>Collection.addAll(Iterator iterator)</code>
+ */
+ public static <E> boolean addAll(Collection<? super E> collection, Iterator<? extends E> iterator, int size) {
+ return iterator.hasNext() ? collection.addAll(list(iterator, size)) : false;
+ }
+
+ /**
+ * Add all the elements in the specified array
+ * to the specified collection.
+ * Return whether the collection changed as a result.
+ * <p>
+ * <code>Collection.addAll(Object[] array)</code>
+ */
+ public static <E> boolean addAll(Collection<? super E> collection, E... array) {
+ return (array.length == 0) ? false : addAll_(collection, array);
+ }
+
+ /**
+ * assume the array is not empty
+ */
+ private static <E> boolean addAll_(Collection<? super E> collection, E... array) {
+ boolean modified = false;
+ for (E element : array) {
+ modified |= collection.add(element);
+ }
+ return modified;
+ }
+
+ /**
+ * Add all the elements returned by the specified iterable
+ * to the specified list at the specified index.
+ * Return whether the list changed as a result.
+ * <p>
+ * <code>List.addAll(Iterable iterable)</code>
+ */
+ public static <E> boolean addAll(List<? super E> list, int index, Iterable<E> iterable) {
+ return addAll(list, index, iterable.iterator());
+ }
+
+ /**
+ * Add all the elements returned by the specified iterable
+ * to the specified list at the specified index.
+ * Return whether the list changed as a result.
+ * <p>
+ * <code>List.addAll(Iterable iterable)</code>
+ */
+ public static <E> boolean addAll(List<? super E> list, int index, Iterable<E> iterable, int size) {
+ return addAll(list, index, iterable.iterator(), size);
+ }
+
+ /**
+ * Add all the elements returned by the specified iterator
+ * to the specified list at the specified index.
+ * Return whether the list changed as a result.
+ * <p>
+ * <code>List.addAll(Iterator iterator)</code>
+ */
+ public static <E> boolean addAll(List<? super E> list, int index, Iterator<? extends E> iterator) {
+ return iterator.hasNext() ? list.addAll(index, list(iterator)) : false;
+ }
+
+ /**
+ * Add all the elements returned by the specified iterator
+ * to the specified list at the specified index.
+ * Return whether the list changed as a result.
+ * <p>
+ * <code>List.addAll(Iterator iterator)</code>
+ */
+ public static <E> boolean addAll(List<? super E> list, int index, Iterator<? extends E> iterator, int size) {
+ return iterator.hasNext() ? list.addAll(index, list(iterator, size)) : false;
+ }
+
+ /**
+ * Add all the elements in the specified array
+ * to the specified list at the specified index.
+ * Return whether the list changed as a result.
+ * <p>
+ * <code>List.addAll(Object[] array)</code>
+ */
+ public static <E> boolean addAll(List<? super E> list, int index, E... array) {
+ return (array.length == 0) ? false : list.addAll(index, Arrays.asList(array));
+ }
+
+
+ // ********** bag **********
+
+ /**
+ * Return a bag corresponding to the specified enumeration.
+ * <p>
+ * <code>HashBag(Enumeration enumeration)</code>
+ */
+ public static <E> HashBag<E> bag(Enumeration<? extends E> enumeration) {
+ return bag(enumeration, new HashBag<E>());
+ }
+
+ /**
+ * Return a bag corresponding to the specified enumeration.
+ * The specified enumeration size is a performance hint.
+ * <p>
+ * <code>HashBag(Enumeration enumeration)</code>
+ */
+ public static <E> HashBag<E> bag(Enumeration<? extends E> enumeration, int enumerationSize) {
+ return bag(enumeration, new HashBag<E>(enumerationSize));
+ }
+
+ private static <E> HashBag<E> bag(Enumeration<? extends E> enumeration, HashBag<E> bag) {
+ while (enumeration.hasMoreElements()) {
+ bag.add(enumeration.nextElement());
+ }
+ return bag;
+ }
+
+ /**
+ * Return a bag corresponding to the specified iterable.
+ * <p>
+ * <code>HashBag(Iterable iterable)</code>
+ */
+ public static <E> HashBag<E> bag(Iterable<? extends E> iterable) {
+ return bag(iterable.iterator());
+ }
+
+ /**
+ * Return a bag corresponding to the specified iterable.
+ * The specified iterable size is a performance hint.
+ * <p>
+ * <code>HashBag(Iterable iterable)</code>
+ */
+ public static <E> HashBag<E> bag(Iterable<? extends E> iterable, int iterableSize) {
+ return bag(iterable.iterator(), iterableSize);
+ }
+
+ /**
+ * Return a bag corresponding to the specified iterator.
+ * <p>
+ * <code>HashBag(Iterator iterator)</code>
+ */
+ public static <E> HashBag<E> bag(Iterator<? extends E> iterator) {
+ return bag(iterator, new HashBag<E>());
+ }
+
+ /**
+ * Return a bag corresponding to the specified iterator.
+ * The specified iterator size is a performance hint.
+ * <p>
+ * <code>HashBag(Iterator iterator)</code>
+ */
+ public static <E> HashBag<E> bag(Iterator<? extends E> iterator, int iteratorSize) {
+ return bag(iterator, new HashBag<E>(iteratorSize));
+ }
+
+ private static <E> HashBag<E> bag(Iterator<? extends E> iterator, HashBag<E> bag) {
+ while (iterator.hasNext()) {
+ bag.add(iterator.next());
+ }
+ return bag;
+ }
+
+ /**
+ * Return a bag corresponding to the specified array.
+ * <p>
+ * <code>HashBag(Object[] array)</code>
+ */
+ public static <E> HashBag<E> bag(E... array) {
+ int len = array.length;
+ HashBag<E> bag = new HashBag<E>(len);
+ for (E item : array) {
+ bag.add(item);
+ }
+ return bag;
+ }
+
+
+ // ********** collection **********
+
+ /**
+ * Return a collection corresponding to the specified enumeration.
+ */
+ public static <E> HashBag<E> collection(Enumeration<? extends E> enumeration) {
+ return bag(enumeration);
+ }
+
+ /**
+ * Return a collection corresponding to the specified enumeration.
+ * The specified enumeration size is a performance hint.
+ */
+ public static <E> HashBag<E> collection(Enumeration<? extends E> enumeration, int enumerationSize) {
+ return bag(enumeration, enumerationSize);
+ }
+
+ /**
+ * Return a collection corresponding to the specified iterable.
+ */
+ public static <E> HashBag<E> collection(Iterable<? extends E> iterable) {
+ return collection(iterable.iterator());
+ }
+
+ /**
+ * Return a collection corresponding to the specified iterable.
+ * The specified iterable size is a performance hint.
+ */
+ public static <E> HashBag<E> collection(Iterable<? extends E> iterable, int iterableSize) {
+ return collection(iterable.iterator(), iterableSize);
+ }
+
+ /**
+ * Return a collection corresponding to the specified iterator.
+ */
+ public static <E> HashBag<E> collection(Iterator<? extends E> iterator) {
+ return bag(iterator);
+ }
+
+ /**
+ * Return a collection corresponding to the specified iterator.
+ * The specified iterator size is a performance hint.
+ */
+ public static <E> HashBag<E> collection(Iterator<? extends E> iterator, int iteratorSize) {
+ return bag(iterator, iteratorSize);
+ }
+
+ /**
+ * Return a collection corresponding to the specified array.
+ */
+ public static <E> HashBag<E> collection(E... array) {
+ return bag(array);
+ }
+
+
+ // ********** contains **********
+
+ /**
+ * Return whether the specified enumeration contains the
+ * specified element.
+ * <p>
+ * <code>Enumeration.contains(Object o)</code>
+ */
+ public static boolean contains(Enumeration<?> enumeration, Object value) {
+ if (value == null) {
+ while (enumeration.hasMoreElements()) {
+ if (enumeration.nextElement() == null) {
+ return true;
+ }
+ }
+ } else {
+ while (enumeration.hasMoreElements()) {
+ if (value.equals(enumeration.nextElement())) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Return whether the specified iterable contains the
+ * specified element.
+ * <p>
+ * <code>Iterable.contains(Object o)</code>
+ */
+ public static boolean contains(Iterable<?> iterable, Object value) {
+ return contains(iterable.iterator(), value);
+ }
+
+ /**
+ * Return whether the specified iterator contains the
+ * specified element.
+ * <p>
+ * <code>Iterator.contains(Object o)</code>
+ */
+ public static boolean contains(Iterator<?> iterator, Object value) {
+ if (value == null) {
+ while (iterator.hasNext()) {
+ if (iterator.next() == null) {
+ return true;
+ }
+ }
+ } else {
+ while (iterator.hasNext()) {
+ if (value.equals(iterator.next())) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+
+ // ********** contains all **********
+
+ /**
+ * Return whether the specified collection contains all of the
+ * elements in the specified iterable.
+ * <p>
+ * <code>Collection.containsAll(Iterable iterable)</code>
+ */
+ public static boolean containsAll(Collection<?> collection, Iterable<?> iterable) {
+ return containsAll(collection, iterable.iterator());
+ }
+
+ /**
+ * Return whether the specified collection contains all of the
+ * elements in the specified iterator.
+ * <p>
+ * <code>Collection.containsAll(Iterator iterator)</code>
+ */
+ public static boolean containsAll(Collection<?> collection, Iterator<?> iterator) {
+ while (iterator.hasNext()) {
+ if ( ! collection.contains(iterator.next())) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Return whether the specified collection contains all of the
+ * elements in the specified array.
+ * <p>
+ * <code>Collection.containsAll(Object[] array)</code>
+ */
+ public static boolean containsAll(Collection<?> collection, Object... array) {
+ for (int i = array.length; i-- > 0; ) {
+ if ( ! collection.contains(array[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Return whether the specified iterable contains all of the
+ * elements in the specified collection.
+ * <p>
+ * <code>Iterable.containsAll(Collection collection)</code>
+ */
+ public static boolean containsAll(Iterable<?> iterable, Collection<?> collection) {
+ return containsAll(iterable.iterator(), collection);
+ }
+
+ /**
+ * Return whether the specified iterable contains all of the
+ * elements in the specified collection.
+ * The specified iterable size is a performance hint.
+ * <p>
+ * <code>Iterable.containsAll(Collection collection)</code>
+ */
+ public static boolean containsAll(Iterable<?> iterable, int iterableSize, Collection<?> collection) {
+ return containsAll(iterable.iterator(), iterableSize, collection);
+ }
+
+ /**
+ * Return whether the specified iterable 1 contains all of the
+ * elements in the specified iterable 2.
+ * <p>
+ * <code>Iterable.containsAll(Iterable iterable)</code>
+ */
+ public static boolean containsAll(Iterable<?> iterable1, Iterable<?> iterable2) {
+ return containsAll(iterable1.iterator(), iterable2.iterator());
+ }
+
+ /**
+ * Return whether the specified iterable 1 contains all of the
+ * elements in the specified iterable 2.
+ * The specified iterable 1 size is a performance hint.
+ * <p>
+ * <code>Iterable.containsAll(Iterable iterable)</code>
+ */
+ public static boolean containsAll(Iterable<?> iterable1, int iterable1Size, Iterable<?> iterable2) {
+ return containsAll(iterable1.iterator(), iterable1Size, iterable2.iterator());
+ }
+
+ /**
+ * Return whether the specified iterable contains all of the
+ * elements in the specified iterator.
+ * <p>
+ * <code>Iterable.containsAll(Iterator iterator)</code>
+ */
+ public static boolean containsAll(Iterable<?> iterable, Iterator<?> iterator) {
+ return containsAll(iterable.iterator(), iterator);
+ }
+
+ /**
+ * Return whether the specified iterable contains all of the
+ * elements in the specified iterator.
+ * The specified iterable size is a performance hint.
+ * <p>
+ * <code>Iterable.containsAll(Iterator iterator)</code>
+ */
+ public static boolean containsAll(Iterable<?> iterable, int iterableSize, Iterator<?> iterator) {
+ return containsAll(iterable.iterator(), iterableSize, iterator);
+ }
+
+ /**
+ * Return whether the specified iterable contains all of the
+ * elements in the specified array.
+ * <p>
+ * <code>Iterable.containsAll(Object[] array)</code>
+ */
+ public static boolean containsAll(Iterable<?> iterable, Object... array) {
+ return containsAll(iterable.iterator(), array);
+ }
+
+ /**
+ * Return whether the specified iterable contains all of the
+ * elements in the specified array.
+ * The specified iterable size is a performance hint.
+ * <p>
+ * <code>Iterable.containsAll(Object[] array)</code>
+ */
+ public static boolean containsAll(Iterable<?> iterable, int iterableSize, Object... array) {
+ return containsAll(iterable.iterator(), iterableSize, array);
+ }
+
+ /**
+ * Return whether the specified iterator contains all of the
+ * elements in the specified collection.
+ * <p>
+ * <code>Iterator.containsAll(Collection collection)</code>
+ */
+ public static boolean containsAll(Iterator<?> iterator, Collection<?> collection) {
+ return set(iterator).containsAll(collection);
+ }
+
+ /**
+ * Return whether the specified iterator contains all of the
+ * elements in the specified collection.
+ * The specified iterator size is a performance hint.
+ * <p>
+ * <code>Iterator.containsAll(Collection collection)</code>
+ */
+ public static boolean containsAll(Iterator<?> iterator, int iteratorSize, Collection<?> collection) {
+ return set(iterator, iteratorSize).containsAll(collection);
+ }
+
+ /**
+ * Return whether the specified iterator contains all of the
+ * elements in the specified iterable.
+ * <p>
+ * <code>Iterator.containsAll(Iterable iterable)</code>
+ */
+ public static boolean containsAll(Iterator<?> iterator, Iterable<?> iterable) {
+ return containsAll(set(iterator), iterable);
+ }
+
+ /**
+ * Return whether the specified iterator contains all of the
+ * elements in the specified iterable.
+ * The specified iterator size is a performance hint.
+ * <p>
+ * <code>Iterator.containsAll(Iterable iterable)</code>
+ */
+ public static boolean containsAll(Iterator<?> iterator, int iteratorSize, Iterable<?> iterable) {
+ return containsAll(set(iterator, iteratorSize), iterable);
+ }
+
+ /**
+ * Return whether the specified iterator 1 contains all of the
+ * elements in the specified iterator 2.
+ * <p>
+ * <code>Iterator.containsAll(Iterator iterator)</code>
+ */
+ public static boolean containsAll(Iterator<?> iterator1, Iterator<?> iterator2) {
+ return containsAll(set(iterator1), iterator2);
+ }
+
+ /**
+ * Return whether the specified iterator 1 contains all of the
+ * elements in the specified iterator 2.
+ * The specified iterator 1 size is a performance hint.
+ * <p>
+ * <code>Iterator.containsAll(Iterator iterator)</code>
+ */
+ public static boolean containsAll(Iterator<?> iterator1, int iterator1Size, Iterator<?> iterator2) {
+ return containsAll(set(iterator1, iterator1Size), iterator2);
+ }
+
+ /**
+ * Return whether the specified iterator contains all of the
+ * elements in the specified array.
+ * <p>
+ * <code>Iterator.containsAll(Object[] array)</code>
+ */
+ public static boolean containsAll(Iterator<?> iterator, Object... array) {
+ return containsAll(set(iterator), array);
+ }
+
+ /**
+ * Return whether the specified iterator contains all of the
+ * elements in the specified array.
+ * The specified iterator size is a performance hint.
+ * <p>
+ * <code>Iterator.containsAll(Object[] array)</code>
+ */
+ public static boolean containsAll(Iterator<?> iterator, int iteratorSize, Object... array) {
+ return containsAll(set(iterator, iteratorSize), array);
+ }
+
+
+ // ********** diff **********
+
+ /**
+ * Return the index of the first elements in the specified
+ * lists that are different, beginning at the end.
+ * If the lists are identical, return -1.
+ * If the lists are different sizes, return the index of the
+ * last element in the longer list.
+ * Use the elements' {@link Object#equals(Object)} method to compare the
+ * elements.
+ * <p>
+ * <code>Collections.diffEnd(List list1, List list2)</code>
+ */
+ public static int diffEnd(List<?> list1, List<?> list2) {
+ return ArrayTools.diffEnd(list1.toArray(), list2.toArray());
+ }
+
+ /**
+ * Return the range of elements in the specified
+ * arrays that are different.
+ * If the arrays are identical, return [size, -1].
+ * Use the elements' {@link Object#equals(Object)} method to compare the
+ * elements.
+ * <p>
+ * <code>Collections.diffRange(List list1, List list2)</code>
+ * @see #diffStart(List, List)
+ * @see #diffEnd(List, List)
+ */
+ public static Range diffRange(List<?> list1, List<?> list2) {
+ return ArrayTools.diffRange(list1.toArray(), list2.toArray());
+ }
+
+ /**
+ * Return the index of the first elements in the specified
+ * lists that are different. If the lists are identical, return
+ * the size of the two lists (i.e. one past the last index).
+ * If the lists are different sizes and all the elements in
+ * the shorter list match their corresponding elements in
+ * the longer list, return the size of the shorter list
+ * (i.e. one past the last index of the shorter list).
+ * Use the elements' {@link Object#equals(Object)} method to compare the
+ * elements.
+ * <p>
+ * <code>Collections.diffStart(List list1, List list2)</code>
+ */
+ public static int diffStart(List<?> list1, List<?> list2) {
+ return ArrayTools.diffStart(list1.toArray(), list2.toArray());
+ }
+
+
+ // ********** identity diff **********
+
+ /**
+ * Return the index of the first elements in the specified
+ * lists that are different, beginning at the end.
+ * If the lists are identical, return -1.
+ * If the lists are different sizes, return the index of the
+ * last element in the longer list.
+ * Use object identity to compare the elements.
+ * <p>
+ * <code>Collections.identityDiffEnd(List list1, List list2)</code>
+ */
+ public static int identityDiffEnd(List<?> list1, List<?> list2) {
+ return ArrayTools.identityDiffEnd(list1.toArray(), list2.toArray());
+ }
+
+ /**
+ * Return the range of elements in the specified
+ * arrays that are different.
+ * If the arrays are identical, return [size, -1].
+ * Use object identity to compare the elements.
+ * <p>
+ * <code>Collections.identityDiffStart(List list1, List list2)</code>
+ * @see #identityDiffStart(List, List)
+ * @see #identityDiffEnd(List, List)
+ */
+ public static Range identityDiffRange(List<?> list1, List<?> list2) {
+ return ArrayTools.identityDiffRange(list1.toArray(), list2.toArray());
+ }
+
+ /**
+ * Return the index of the first elements in the specified
+ * lists that are different. If the lists are identical, return
+ * the size of the two lists (i.e. one past the last index).
+ * If the lists are different sizes and all the elements in
+ * the shorter list match their corresponding elements in
+ * the longer list, return the size of the shorter list
+ * (i.e. one past the last index of the shorter list).
+ * Use object identity to compare the elements.
+ * <p>
+ * <code>Collections.identityDiffStart(List list1, List list2)</code>
+ */
+ public static int identityDiffStart(List<?> list1, List<?> list2) {
+ return ArrayTools.identityDiffStart(list1.toArray(), list2.toArray());
+ }
+
+
+ // ********** elements are equal **********
+
+ /**
+ * Return whether the specified iterables do not return the same elements
+ * in the same order.
+ */
+ public static boolean elementsAreDifferent(Iterable<?> iterable1, Iterable<?> iterable2) {
+ return elementsAreDifferent(iterable1.iterator(), iterable2.iterator());
+ }
+
+ /**
+ * Return whether the specified iterators do not return the same elements
+ * in the same order.
+ */
+ public static boolean elementsAreDifferent(Iterator<?> iterator1, Iterator<?> iterator2) {
+ return ! elementsAreEqual(iterator1, iterator2);
+ }
+
+ /**
+ * Return whether the specified iterables return equal elements
+ * in the same order.
+ * <p>
+ * <code>Iterable.elementsAreEqual(Iterable iterable)</code>
+ */
+ public static boolean elementsAreEqual(Iterable<?> iterable1, Iterable<?> iterable2) {
+ return elementsAreEqual(iterable1.iterator(), iterable2.iterator());
+ }
+
+ /**
+ * Return whether the specified iterators return equal elements
+ * in the same order.
+ * <p>
+ * <code>Iterator.elementsAreEqual(Iterator iterator)</code>
+ */
+ public static boolean elementsAreEqual(Iterator<?> iterator1, Iterator<?> iterator2) {
+ while (iterator1.hasNext() && iterator2.hasNext()) {
+ if (Tools.valuesAreDifferent(iterator1.next(), iterator2.next())) {
+ return false;
+ }
+ }
+ return ! (iterator1.hasNext() || iterator2.hasNext());
+ }
+
+
+ // ********** elements are identical **********
+
+ /**
+ * Return whether the specified iterables return the same elements.
+ * <p>
+ * <code>Iterable.identical(Iterable iterable)</code>
+ */
+ public static boolean elementsAreIdentical(Iterable<?> iterable1, Iterable<?> iterable2) {
+ return elementsAreIdentical(iterable1.iterator(), iterable2.iterator());
+ }
+
+ /**
+ * Return whether the specified iterators return the same elements.
+ * <p>
+ * <code>Iterator.identical(Iterator iterator)</code>
+ */
+ public static boolean elementsAreIdentical(Iterator<?> iterator1, Iterator<?> iterator2) {
+ while (iterator1.hasNext() && iterator2.hasNext()) {
+ if (iterator1.next() != iterator2.next()) {
+ return false;
+ }
+ }
+ return ! (iterator1.hasNext() || iterator2.hasNext());
+ }
+
+
+ // ********** get **********
+
+ /**
+ * Return the element corresponding to the specified index
+ * in the specified iterable.
+ * <p>
+ * <code>Iterable.get(int index)</code>
+ */
+ public static <E> E get(Iterable<? extends E> iterable, int index) {
+ return get(iterable.iterator(), index);
+ }
+
+ /**
+ * Return the element corresponding to the specified index
+ * in the specified iterator.
+ * <p>
+ * <code>Iterator.get(int index)</code>
+ */
+ public static <E> E get(Iterator<? extends E> iterator, int index) {
+ int i = 0;
+ while (iterator.hasNext()) {
+ E next = iterator.next();
+ if (i++ == index) {
+ return next;
+ }
+ }
+ throw new IndexOutOfBoundsException(String.valueOf(index) + ':' + String.valueOf(i));
+ }
+
+
+ // ********** hash code **********
+
+ public static int hashCode(Iterable<?> iterable) {
+ if (iterable == null) {
+ return 0;
+ }
+ int hash = 1;
+ for (Object element : iterable) {
+ hash = 31 * hash + (element == null ? 0 : element.hashCode());
+ }
+ return hash;
+ }
+
+
+ // ********** index of **********
+
+ /**
+ * Return the index of the first occurrence of the
+ * specified element in the specified iterable;
+ * return -1 if there is no such index.
+ * <p>
+ * <code>Iterable.indexOf(Object o)</code>
+ */
+ public static int indexOf(Iterable<?> iterable, Object value) {
+ return indexOf(iterable.iterator(), value);
+ }
+
+ /**
+ * Return the index of the first occurrence of the
+ * specified element in the specified iterator;
+ * return -1 if there is no such index.
+ * <p>
+ * <code>Iterator.indexOf(Object o)</code>
+ */
+ public static int indexOf(Iterator<?> iterator, Object value) {
+ if (value == null) {
+ for (int i = 0; iterator.hasNext(); i++) {
+ if (iterator.next() == null) {
+ return i;
+ }
+ }
+ } else {
+ for (int i = 0; iterator.hasNext(); i++) {
+ if (value.equals(iterator.next())) {
+ return i;
+ }
+ }
+ }
+ return -1;
+ }
+
+
+ // ********** insertion index of **********
+
+ /**
+ * Return an index of where the specified comparable object
+ * can be inserted into the specified sorted list and still keep
+ * the list sorted. If the specified sorted list is an instance of
+ * {@link RandomAccess} return the <em>maximum</em> insertion index;
+ * otherwise return the <em>minimum</em> insertion index.
+ */
+ public static <E extends Comparable<? super E>> int insertionIndexOf(List<E> sortedList, Comparable<E> value) {
+ if (sortedList instanceof RandomAccess) {
+ for (int i = sortedList.size(); i-- > 0; ) {
+ if (value.compareTo(sortedList.get(i)) >= 0) {
+ return i + 1;
+ }
+ }
+ return 0;
+ }
+ int i = 0;
+ for (E element : sortedList) {
+ if (value.compareTo(element) <= 0) {
+ return i;
+ }
+ i++;
+ }
+ return i;
+ }
+
+ /**
+ * Return an index of where the specified comparable object
+ * can be inserted into the specified sorted list and still keep
+ * the list sorted. If the specified sorted list is an instance of
+ * {@link RandomAccess} return the <em>maximum</em> insertion index;
+ * otherwise return the <em>minimum</em> insertion index.
+ */
+ public static <E> int insertionIndexOf(List<E> sortedList, E value, Comparator<? super E> comparator) {
+ if (sortedList instanceof RandomAccess) {
+ for (int i = sortedList.size(); i-- > 0; ) {
+ if (comparator.compare(value, sortedList.get(i)) >= 0) {
+ return i + 1;
+ }
+ }
+ return 0;
+ }
+ int i = 0;
+ for (E element : sortedList) {
+ if (comparator.compare(value, element) <= 0) {
+ return i;
+ }
+ i++;
+ }
+ return i;
+ }
+
+
+ // ********** iterable/iterator **********
+
+ /**
+ * Return an iterable on the elements in the specified array.
+ * <p>
+ * <code>Arrays.iterable(Object[] array)</code>
+ */
+ public static <E> Iterable<E> iterable(E... array) {
+ return new ArrayIterable<E>(array);
+ }
+
+ /**
+ * Return an iterator on the elements in the specified array.
+ * <p>
+ * <code>Arrays.iterator(Object[] array)</code>
+ */
+ public static <E> Iterator<E> iterator(E... array) {
+ return new ArrayIterator<E>(array);
+ }
+
+
+ // ********** last **********
+
+ /**
+ * Return the specified iterable's last element.
+ * <p>
+ * <code>Iterable.last()</code>
+ *
+ * @exception java.util.NoSuchElementException iterable is empty.
+ */
+ public static <E> E last(Iterable<E> iterable) {
+ return last(iterable.iterator());
+ }
+
+ /**
+ * Return the specified iterator's last element.
+ * <p>
+ * <code>Iterator.last()</code>
+ *
+ * @exception java.util.NoSuchElementException iterator is empty.
+ */
+ public static <E> E last(Iterator<E> iterator) {
+ E last;
+ do {
+ last = iterator.next();
+ } while (iterator.hasNext());
+ return last;
+ }
+
+
+ // ********** last index of **********
+
+ /**
+ * Return the index of the last occurrence of the
+ * specified element in the specified iterable;
+ * return -1 if there is no such index.
+ * <p>
+ * <code>Iterable.lastIndexOf(Object o)
+ */
+ public static int lastIndexOf(Iterable<?> iterable, Object value) {
+ return lastIndexOf(iterable.iterator(), value);
+ }
+
+ /**
+ * Return the index of the last occurrence of the
+ * specified element in the specified iterable;
+ * return -1 if there is no such index.
+ * The specified iterable size is a performance hint.
+ * <p>
+ * <code>Iterable.lastIndexOf(Object o)
+ */
+ public static int lastIndexOf(Iterable<?> iterable, int iterableSize, Object value) {
+ return lastIndexOf(iterable.iterator(), iterableSize, value);
+ }
+
+ /**
+ * Return the index of the last occurrence of the
+ * specified element in the specified iterator;
+ * return -1 if there is no such index.
+ * <p>
+ * <code>Iterator.lastIndexOf(Object o)
+ */
+ public static int lastIndexOf(Iterator<?> iterator, Object value) {
+ return iterator.hasNext() ? list(iterator).lastIndexOf(value) : -1;
+ }
+
+ /**
+ * Return the index of the last occurrence of the
+ * specified element in the specified iterator;
+ * return -1 if there is no such index.
+ * The specified iterator size is a performance hint.
+ * <p>
+ * <code>Iterator.lastIndexOf(Object o)
+ */
+ public static int lastIndexOf(Iterator<?> iterator, int iteratorSize, Object value) {
+ return iterator.hasNext() ? list(iterator, iteratorSize).lastIndexOf(value) : -1;
+ }
+
+
+ // ********** list **********
+
+ /**
+ * Return a list corresponding to the specified iterable.
+ * <p>
+ * <code>Iterable.toList()</code>
+ */
+ public static <E> ArrayList<E> list(Iterable<? extends E> iterable) {
+ return list(iterable.iterator());
+ }
+
+ /**
+ * Return a list corresponding to the specified iterable.
+ * The specified iterable size is a performance hint.
+ * <p>
+ * <code>Iterable.toList()</code>
+ */
+ public static <E> ArrayList<E> list(Iterable<? extends E> iterable, int iterableSize) {
+ return list(iterable.iterator(), iterableSize);
+ }
+
+ /**
+ * Return a list corresponding to the specified iterator.
+ * <p>
+ * <code>Iterator.toList()</code>
+ */
+ public static <E> ArrayList<E> list(Iterator<? extends E> iterator) {
+ return list(iterator, new ArrayList<E>());
+ }
+
+ /**
+ * Return a list corresponding to the specified iterator.
+ * The specified iterator size is a performance hint.
+ * <p>
+ * <code>Iterator.toList()</code>
+ */
+ public static <E> ArrayList<E> list(Iterator<? extends E> iterator, int iteratorSize) {
+ return list(iterator, new ArrayList<E>(iteratorSize));
+ }
+
+ private static <E> ArrayList<E> list(Iterator<? extends E> iterator, ArrayList<E> list) {
+ while (iterator.hasNext()) {
+ list.add(iterator.next());
+ }
+ return list;
+ }
+
+ /**
+ * Return a list corresponding to the specified array.
+ * Unlike {@link Arrays#asList(Object[])}, the list
+ * is modifiable and is not backed by the array.
+ */
+ public static <E> ArrayList<E> list(E... array) {
+ return new ArrayList<E>(Arrays.asList(array));
+ }
+
+ /**
+ * Return a list iterator for the specified array.
+ * <p>
+ * <code>Arrays.listIterator(Object[] array)</code>
+ */
+ public static <E> ListIterator<E> listIterator(E... array) {
+ return listIterator(array, 0);
+ }
+
+ /**
+ * Return a list iterator for the specified array
+ * starting at the specified position in the array.
+ * <p>
+ * <code>Arrays.listIterator(Object[] array, int index)</code>
+ */
+ public static <E> ListIterator<E> listIterator(E[] array, int start) {
+ return listIterator(array, start, array.length - start);
+ }
+
+ /**
+ * Return a list iterator for the specified array
+ * starting at the specified position in the array.
+ * <p>
+ * <code>Arrays.listIterator(Object[] array, int index, int length)</code>
+ */
+ public static <E> ListIterator<E> listIterator(E[] array, int start, int length) {
+ return new ArrayListIterator<E>(array, start, length);
+ }
+
+
+ // ********** move **********
+
+ /**
+ * Move an element from the specified source index to the specified target
+ * index. Return the altered list.
+ * <p>
+ * <code>List.move(int targetIndex, int sourceIndex)</code>
+ */
+ public static <E> List<E> move(List<E> list, int targetIndex, int sourceIndex) {
+ return (targetIndex == sourceIndex) ? list : move_(list, targetIndex, sourceIndex);
+ }
+
+ /**
+ * assume targetIndex != sourceIndex
+ */
+ private static <E> List<E> move_(List<E> list, int targetIndex, int sourceIndex) {
+ if (list instanceof RandomAccess) {
+ // move elements, leaving the list in place
+ E temp = list.get(sourceIndex);
+ if (targetIndex < sourceIndex) {
+ for (int i = sourceIndex; i-- > targetIndex; ) {
+ list.set(i + 1, list.get(i));
+ }
+ } else {
+ for (int i = sourceIndex; i < targetIndex; i++) {
+ list.set(i, list.get(i + 1));
+ }
+ }
+ list.set(targetIndex, temp);
+ } else {
+ // remove the element and re-add it at the target index
+ list.add(targetIndex, list.remove(sourceIndex));
+ }
+ return list;
+ }
+
+ /**
+ * Move elements from the specified source index to the specified target
+ * index. Return the altered list.
+ * <p>
+ * <code>List.move(int targetIndex, int sourceIndex, int length)</code>
+ */
+ public static <E> List<E> move(List<E> list, int targetIndex, int sourceIndex, int length) {
+ if ((targetIndex == sourceIndex) || (length == 0)) {
+ return list;
+ }
+ if (length == 1) {
+ return move_(list, targetIndex, sourceIndex);
+ }
+ if (list instanceof RandomAccess) {
+ // move elements, leaving the list in place
+ ArrayList<E> temp = new ArrayList<E>(list.subList(sourceIndex, sourceIndex + length));
+ if (targetIndex < sourceIndex) {
+ for (int i = sourceIndex; i-- > targetIndex; ) {
+ list.set(i + length, list.get(i));
+ }
+ } else {
+ for (int i = sourceIndex; i < targetIndex; i++) {
+ list.set(i, list.get(i + length));
+ }
+ }
+ for (int i = 0; i < length; i++) {
+ list.set(targetIndex + i, temp.get(i));
+ }
+ } else {
+ // remove the elements and re-add them at the target index
+ list.addAll(targetIndex, removeElementsAtIndex(list, sourceIndex, length));
+ }
+ return list;
+ }
+
+
+ // ********** remove all **********
+
+ /**
+ * Remove all the elements returned by the specified iterable
+ * from the specified collection.
+ * Return whether the collection changed as a result.
+ * <p>
+ * <code>Collection.removeAll(Iterable iterable)</code>
+ */
+ public static boolean removeAll(Collection<?> collection, Iterable<?> iterable) {
+ return removeAll(collection, iterable.iterator());
+ }
+
+ /**
+ * Remove all the elements returned by the specified iterable
+ * from the specified collection.
+ * Return whether the collection changed as a result.
+ * The specified iterable size is a performance hint.
+ * <p>
+ * <code>Collection.removeAll(Iterable iterable)</code>
+ */
+ public static boolean removeAll(Collection<?> collection, Iterable<?> iterable, int iterableSize) {
+ return removeAll(collection, iterable.iterator(), iterableSize);
+ }
+
+ /**
+ * Remove all the elements returned by the specified iterator
+ * from the specified collection.
+ * Return whether the collection changed as a result.
+ * <p>
+ * <code>Collection.removeAll(Iterator iterator)</code>
+ */
+ public static boolean removeAll(Collection<?> collection, Iterator<?> iterator) {
+ return iterator.hasNext() ? collection.removeAll(set(iterator)) : false;
+ }
+
+ /**
+ * Remove all the elements returned by the specified iterator
+ * from the specified collection.
+ * Return whether the collection changed as a result.
+ * The specified iterator size is a performance hint.
+ * <p>
+ * <code>Collection.removeAll(Iterator iterator)</code>
+ */
+ public static boolean removeAll(Collection<?> collection, Iterator<?> iterator, int iteratorSize) {
+ return iterator.hasNext() ? collection.removeAll(set(iterator, iteratorSize)) : false;
+ }
+
+ /**
+ * Remove all the elements in the specified array
+ * from the specified collection.
+ * Return whether the collection changed as a result.
+ * <p>
+ * <code>Collection.removeAll(Object[] array)</code>
+ */
+ public static boolean removeAll(Collection<?> collection, Object... array) {
+ return (array.length == 0) ? false : collection.removeAll(set(array));
+ }
+
+
+ // ********** remove all occurrences **********
+
+ /**
+ * Remove all occurrences of the specified element
+ * from the specified collection.
+ * Return whether the collection changed as a result.
+ * <p>
+ * <code>Collection.removeAllOccurrences(Object value)</code>
+ */
+ public static boolean removeAllOccurrences(Collection<?> collection, Object value) {
+ boolean modified = false;
+ Iterator<?> stream = collection.iterator();
+ if (value == null) {
+ while (stream.hasNext()) {
+ if (stream.next() == null) {
+ stream.remove();
+ modified = true;
+ }
+ }
+ } else {
+ while (stream.hasNext()) {
+ if (value.equals(stream.next())) {
+ stream.remove();
+ modified = true;
+ }
+ }
+ }
+ return modified;
+ }
+
+
+ // ********** remove elements at index **********
+
+ /**
+ * Remove the elements at the specified index.
+ * Return the removed elements.
+ * <p>
+ * <code>List.remove(int index, int length)</code>
+ */
+ public static <E> ArrayList<E> removeElementsAtIndex(List<E> list, int index, int length) {
+ List<E> subList = list.subList(index, index + length);
+ ArrayList<E> result = new ArrayList<E>(subList);
+ subList.clear();
+ return result;
+ }
+
+
+ // ********** remove duplicate elements **********
+
+ /**
+ * Remove any duplicate elements from the specified list,
+ * while maintaining the order.
+ * Return whether the list changed as a result.
+ */
+ public static <E> boolean removeDuplicateElements(List<E> list) {
+ int size = list.size();
+ if ((size == 0) || (size == 1)) {
+ return false;
+ }
+ return removeDuplicateElements(list, size);
+ }
+
+ /**
+ * assume list is non-empty
+ */
+ static <E> boolean removeDuplicateElements(List<E> list, int size) {
+ LinkedHashSet<E> temp = new LinkedHashSet<E>(size); // take advantage of hashed look-up
+ boolean modified = false;
+ for (E item : list) {
+ if ( ! temp.add(item)) {
+ modified = true; // duplicate item
+ }
+ }
+ if (modified) {
+ int i = 0;
+ for (E e : temp) {
+ list.set(i, e);
+ i++;
+ }
+ int tempSize = temp.size();
+ for (i = list.size(); i-- > tempSize; ) {
+ list.remove(i); // pull off the end
+ }
+ }
+ return modified;
+ }
+
+
+ // ********** retain all **********
+
+ /**
+ * Retain only the elements in the specified iterable
+ * in the specified collection.
+ * Return whether the collection changed as a result.
+ * <p>
+ * <code>Collection.retainAll(Iterable iterable)</code>
+ */
+ public static boolean retainAll(Collection<?> collection, Iterable<?> iterable) {
+ return retainAll(collection, iterable.iterator());
+ }
+
+ /**
+ * Retain only the elements in the specified iterable
+ * in the specified collection.
+ * Return whether the collection changed as a result.
+ * The specified iterable size is a performance hint.
+ * <p>
+ * <code>Collection.retainAll(Iterable iterable)</code>
+ */
+ public static boolean retainAll(Collection<?> collection, Iterable<?> iterable, int iterableSize) {
+ return retainAll(collection, iterable.iterator(), iterableSize);
+ }
+
+ /**
+ * Retain only the elements in the specified iterator
+ * in the specified collection.
+ * Return whether the collection changed as a result.
+ * <p>
+ * <code>Collection.retainAll(Iterator iterator)</code>
+ */
+ public static boolean retainAll(Collection<?> collection, Iterator<?> iterator) {
+ if (iterator.hasNext()) {
+ return collection.retainAll(set(iterator));
+ }
+ if (collection.isEmpty()) {
+ return false;
+ }
+ collection.clear();
+ return true;
+ }
+
+ /**
+ * Retain only the elements in the specified iterator
+ * in the specified collection.
+ * Return whether the collection changed as a result.
+ * The specified iterator size is a performance hint.
+ * <p>
+ * <code>Collection.retainAll(Iterator iterator)</code>
+ */
+ public static boolean retainAll(Collection<?> collection, Iterator<?> iterator, int iteratorSize) {
+ if (iterator.hasNext()) {
+ return collection.retainAll(set(iterator, iteratorSize));
+ }
+ if (collection.isEmpty()) {
+ return false;
+ }
+ collection.clear();
+ return true;
+ }
+
+ /**
+ * Retain only the elements in the specified array
+ * in the specified collection.
+ * Return whether the collection changed as a result.
+ * <p>
+ * <code>Collection.retainAll(Object[] array)</code>
+ */
+ public static boolean retainAll(Collection<?> collection, Object... array) {
+ if (array.length > 0) {
+ return collection.retainAll(set(array));
+ }
+ if (collection.isEmpty()) {
+ return false;
+ }
+ collection.clear();
+ return true;
+ }
+
+
+ // ********** reverse list **********
+
+ /**
+ * Return a list with entries in reverse order from those
+ * returned by the specified iterable.
+ * <p>
+ * <code>Iterable.reverseList()</code>
+ */
+ public static <E> ArrayList<E> reverseList(Iterable<? extends E> iterable) {
+ return reverseList(iterable.iterator());
+ }
+
+ /**
+ * Return a list with entries in reverse order from those
+ * returned by the specified iterable.
+ * The specified iterable size is a performance hint.
+ * <p>
+ * <code>Iterable.reverseList()</code>
+ */
+ public static <E> ArrayList<E> reverseList(Iterable<? extends E> iterable, int iterableSize) {
+ return reverseList(iterable.iterator(), iterableSize);
+ }
+
+ /**
+ * Return a list with entries in reverse order from those
+ * returned by the specified iterator.
+ * <p>
+ * <code>Iterator.reverseList()</code>
+ */
+ public static <E> ArrayList<E> reverseList(Iterator<? extends E> iterator) {
+ return (ArrayList<E>) reverse(list(iterator));
+ }
+
+ /**
+ * Return a list with entries in reverse order from those
+ * returned by the specified iterator.
+ * The specified iterator size is a performance hint.
+ * <p>
+ * <code>Iterator.reverseList()</code>
+ */
+ public static <E> ArrayList<E> reverseList(Iterator<? extends E> iterator, int size) {
+ return (ArrayList<E>) reverse(list(iterator, size));
+ }
+
+
+ // ********** rotate **********
+
+ /**
+ * Return the list after it has been "rotated" by one position.
+ * <p>
+ * <code>List.rotate()</code>
+ */
+ public static <E> List<E> rotate(List<E> list) {
+ return rotate(list, 1);
+ }
+
+
+ // ********** set **********
+
+ /**
+ * Return a set corresponding to the specified iterable.
+ * <p>
+ * <code>HashSet(Iterable iterable)</code>
+ */
+ public static <E> HashSet<E> set(Iterable<? extends E> iterable) {
+ return set(iterable.iterator());
+ }
+
+ /**
+ * Return a set corresponding to the specified iterable.
+ * The specified iterable size is a performance hint.
+ * <p>
+ * <code>HashSet(Iterable iterable)</code>
+ */
+ public static <E> HashSet<E> set(Iterable<? extends E> iterable, int iterableSize) {
+ return set(iterable.iterator(), iterableSize);
+ }
+
+ /**
+ * Return a set corresponding to the specified iterator.
+ * <p>
+ * <code>HashSet(Iterator iterator)</code>
+ */
+ public static <E> HashSet<E> set(Iterator<? extends E> iterator) {
+ return set(iterator, new HashSet<E>());
+ }
+
+ /**
+ * Return a set corresponding to the specified iterator.
+ * The specified iterator size is a performance hint.
+ * <p>
+ * <code>HashSet(Iterator iterator)</code>
+ */
+ public static <E> HashSet<E> set(Iterator<? extends E> iterator, int iteratorSize) {
+ return set(iterator, new HashSet<E>(iteratorSize));
+ }
+
+ private static <E> HashSet<E> set(Iterator<? extends E> iterator, HashSet<E> set) {
+ while (iterator.hasNext()) {
+ set.add(iterator.next());
+ }
+ return set;
+ }
+
+ /**
+ * Return a set corresponding to the specified array.
+ * <p>
+ * <code>HashSet(Object[] array)</code>
+ */
+ public static <E> HashSet<E> set(E... array) {
+ HashSet<E> set = new HashSet<E>(array.length);
+ for (int i = array.length; i-- > 0;) {
+ set.add(array[i]);
+ }
+ return set;
+ }
+
+
+ // ********** singleton iterator **********
+
+ /**
+ * Return an iterator that returns only the single,
+ * specified object.
+ * <p>
+ * <code>Object.toIterator()</code>
+ */
+ public static <E> Iterator<E> singletonIterator(E value) {
+ return new SingleElementIterator<E>(value);
+ }
+
+ /**
+ * Return a list iterator that returns only the single,
+ * specified object.
+ * <p>
+ * <code>Object.toListIterator()</code>
+ */
+ public static <E> ListIterator<E> singletonListIterator(E value) {
+ return new SingleElementListIterator<E>(value);
+ }
+
+
+ // ********** size **********
+
+ /**
+ * Return the number of elements returned by the specified iterable.
+ * <p>
+ * <code>Iterable.size()</code>
+ */
+ public static int size(Iterable<?> iterable) {
+ return size(iterable.iterator());
+ }
+
+ /**
+ * Return the number of elements returned by the specified iterator.
+ * <p>
+ * <code>Iterator.size()</code>
+ */
+ public static int size(Iterator<?> iterator) {
+ int size = 0;
+ while (iterator.hasNext()) {
+ iterator.next();
+ size++;
+ }
+ return size;
+ }
+
+ /**
+ * Return whether the specified iterable is empty
+ * (Shortcuts the iterator rather than calculating the entire size)
+ */
+ public static boolean isEmpty(Iterable<?> iterable) {
+ return isEmpty(iterable.iterator());
+ }
+
+ /**
+ * Return whether the specified iterator is empty
+ * (Shortcuts the iterator rather than calculating the entire size)
+ */
+ public static boolean isEmpty(Iterator<?> iterator) {
+ return ! iterator.hasNext();
+ }
+
+
+ // ********** sort **********
+
+ /**
+ * Return an iterable containing the sorted elements of the specified iterable.
+ * <p>
+ * <code>Iterable.sort()</code>
+ */
+ public static <E extends Comparable<? super E>> Iterable<E> sort(Iterable<E> iterable) {
+ return sort(iterable, null);
+ }
+
+ /**
+ * Return an iterable containing the sorted elements of the specified iterable.
+ * The specified iterable size is a performance hint.
+ * <p>
+ * <code>Iterable.sort()</code>
+ */
+ public static <E extends Comparable<? super E>> Iterable<E> sort(Iterable<E> iterable, int iterableSize) {
+ return sort(iterable, null, iterableSize);
+ }
+
+ /**
+ * Return an iterable containing the sorted elements of the specified iterable.
+ * <p>
+ * <code>Iterable.sort(Comparator comparator)</code>
+ */
+ public static <E> Iterable<E> sort(Iterable<E> iterable, Comparator<? super E> comparator) {
+ return sort(list(iterable), comparator);
+ }
+
+ /**
+ * Return an iterable containing the sorted elements of the specified iterable.
+ * The specified iterable size is a performance hint.
+ * <p>
+ * <code>Iterable.sort(Comparator comparator)</code>
+ */
+ public static <E> Iterable<E> sort(Iterable<E> iterable, Comparator<? super E> comparator, int iterableSize) {
+ return sort(list(iterable, iterableSize), comparator);
+ }
+
+ /**
+ * Return the iterator after it has been "sorted".
+ * <p>
+ * <code>Iterator.sort()</code>
+ */
+ public static <E extends Comparable<? super E>> ListIterator<E> sort(Iterator<? extends E> iterator) {
+ return sort(iterator, null);
+ }
+
+ /**
+ * Return the iterator after it has been "sorted".
+ * The specified iterator size is a performance hint.
+ * <p>
+ * <code>Iterator.sort()</code>
+ */
+ public static <E extends Comparable<? super E>> ListIterator<E> sort(Iterator<? extends E> iterator, int iteratorSize) {
+ return sort(iterator, null, iteratorSize);
+ }
+
+ /**
+ * Return the iterator after it has been "sorted".
+ * <p>
+ * <code>Iterator.sort(Comparator comparator)</code>
+ */
+ public static <E> ListIterator<E> sort(Iterator<? extends E> iterator, Comparator<? super E> comparator) {
+ return sort(list(iterator), comparator).listIterator();
+ }
+
+ /**
+ * Return the iterator after it has been "sorted".
+ * The specified iterator size is a performance hint.
+ * <p>
+ * <code>Iterator.sort(Comparator comparator)</code>
+ */
+ public static <E> ListIterator<E> sort(Iterator<? extends E> iterator, Comparator<? super E> comparator, int iteratorSize) {
+ return sort(list(iterator, iteratorSize), comparator).listIterator();
+ }
+
+
+ // ********** sorted set **********
+
+ /**
+ * Return a sorted set corresponding to the specified iterable.
+ * <p>
+ * <code>TreeSet(Iterable iterable)</code>
+ */
+ public static <E extends Comparable<? super E>> TreeSet<E> sortedSet(Iterable<? extends E> iterable) {
+ return sortedSet(iterable.iterator());
+ }
+
+ /**
+ * Return a sorted set corresponding to the specified iterable.
+ * The specified iterable size is a performance hint.
+ * <p>
+ * <code>TreeSet(Iterable iterable)</code>
+ */
+ public static <E extends Comparable<? super E>> TreeSet<E> sortedSet(Iterable<? extends E> iterable, int iterableSize) {
+ return sortedSet(iterable.iterator(), iterableSize);
+ }
+
+ /**
+ * Return a sorted set corresponding to the specified iterable
+ * and comparator.
+ * <p>
+ * <code>TreeSet(Iterable iterable, Comparator c)</code>
+ */
+ public static <E> TreeSet<E> sortedSet(Iterable<? extends E> iterable, Comparator<? super E> comparator) {
+ return sortedSet(iterable.iterator(), comparator);
+ }
+
+ /**
+ * Return a sorted set corresponding to the specified iterable
+ * and comparator.
+ * The specified iterable size is a performance hint.
+ * <p>
+ * <code>TreeSet(Iterable iterable, Comparator c)</code>
+ */
+ public static <E> TreeSet<E> sortedSet(Iterable<? extends E> iterable, Comparator<? super E> comparator, int iterableSize) {
+ return sortedSet(iterable.iterator(), comparator, iterableSize);
+ }
+
+ /**
+ * Return a sorted set corresponding to the specified iterator.
+ * <p>
+ * <code>TreeSet(Iterator iterator)</code>
+ */
+ public static <E extends Comparable<? super E>> TreeSet<E> sortedSet(Iterator<? extends E> iterator) {
+ return sortedSet(iterator, null);
+ }
+
+ /**
+ * Return a sorted set corresponding to the specified iterator.
+ * The specified iterator size is a performance hint.
+ * <p>
+ * <code>TreeSet(Iterator iterator)</code>
+ */
+ public static <E extends Comparable<? super E>> TreeSet<E> sortedSet(Iterator<? extends E> iterator, int iteratorSize) {
+ return sortedSet(iterator, null, iteratorSize);
+ }
+
+ /**
+ * Return a sorted set corresponding to the specified iterator
+ * and comparator.
+ * <p>
+ * <code>TreeSet(Iterator iterator, Comparator c)</code>
+ */
+ public static <E> TreeSet<E> sortedSet(Iterator<? extends E> iterator, Comparator<? super E> comparator) {
+ return sortedSet(list(iterator), comparator);
+ }
+
+ /**
+ * Return a sorted set corresponding to the specified iterator
+ * and comparator.
+ * The specified iterator size is a performance hint.
+ * <p>
+ * <code>TreeSet(Iterator iterator, Comparator c)</code>
+ */
+ public static <E> TreeSet<E> sortedSet(Iterator<? extends E> iterator, Comparator<? super E> comparator, int iteratorSize) {
+ return sortedSet(list(iterator, iteratorSize), comparator);
+ }
+
+ private static <E> TreeSet<E> sortedSet(List<E> list, Comparator<? super E> comparator) {
+ TreeSet<E> sortedSet = new TreeSet<E>(comparator);
+ sortedSet.addAll(list);
+ return sortedSet;
+ }
+
+ /**
+ * Return a sorted set corresponding to the specified array.
+ * <p>
+ * <code>TreeSet(Object[] array)</code>
+ */
+ public static <E extends Comparable<? super E>> TreeSet<E> sortedSet(E... array) {
+ return sortedSet(array, null);
+ }
+
+ /**
+ * Return a sorted set corresponding to the specified array
+ * and comparator.
+ * <p>
+ * <code>TreeSet(Object[] array, Comparator c)</code>
+ */
+ public static <E> TreeSet<E> sortedSet(E[] array, Comparator<? super E> comparator) {
+ TreeSet<E> sortedSet = new TreeSet<E>(comparator);
+ sortedSet.addAll(Arrays.asList(array));
+ return sortedSet;
+ }
+
+
+ // ********** Old School Vector **********
+
+ /**
+ * Return a vector corresponding to the specified iterable.
+ * This is useful for legacy code that requires a {@link Vector}.
+ * <p>
+ * <code>Vector(Iterable iterable)</code>
+ */
+ public static <E> Vector<E> vector(Iterable<? extends E> iterable) {
+ return vector(iterable.iterator());
+ }
+
+ /**
+ * Return a vector corresponding to the specified iterable.
+ * This is useful for legacy code that requires a {@link Vector}.
+ * <p>
+ * <code>Vector(Iterable iterable, int size)</code>
+ */
+ public static <E> Vector<E> vector(Iterable<? extends E> iterable, int size) {
+ return vector(iterable.iterator(), size);
+ }
+
+ /**
+ * Return a vector corresponding to the specified iterator.
+ * This is useful for legacy code that requires a {@link Vector}.
+ * <p>
+ * <code>Vector(Iterator iterator)</code>
+ */
+ public static <E> Vector<E> vector(Iterator<? extends E> iterator) {
+ return vector(iterator, new Vector<E>());
+ }
+
+ /**
+ * Return a vector corresponding to the specified iterator.
+ * This is useful for legacy code that requires a {@link Vector}.
+ * <p>
+ * <code>Vector(Iterator iterator, int size)</code>
+ */
+ public static <E> Vector<E> vector(Iterator<? extends E> iterator, int size) {
+ return vector(iterator, new Vector<E>(size));
+ }
+
+ private static <E> Vector<E> vector(Iterator<? extends E> iterator, Vector<E> v) {
+ while (iterator.hasNext()) {
+ v.addElement(iterator.next());
+ }
+ return v;
+ }
+
+ /**
+ * Return a vector corresponding to the specified array.
+ * This is useful for legacy code that requires a {@link Vector}.
+ * <p>
+ * <code>Vector(Object... array)</code>
+ */
+ public static <E> Vector<E> vector(E... array) {
+ Vector<E> v = new Vector<E>(array.length);
+ for (E item : array) {
+ v.addElement(item);
+ }
+ return v;
+ }
+
+
+ // ********** single-use Iterable **********
+
+ /**
+ * Return a one-use {@link Iterable} for the specified {@link Iterator}.
+ * Throw an {@link IllegalStateException} if {@link Iterable#iterator()}
+ * is called more than once.
+ * As such, this utility should only be used in one-use situations, such as
+ * a foreach loop.
+ */
+ public static <E> Iterable<E> iterable(Iterator<? extends E> iterator) {
+ return new SingleUseIterable<E>(iterator);
+ }
+
+ /**
+ * This is a one-time use iterable that can return a single iterator.
+ * Once the iterator is returned the iterable is no longer valid.
+ * As such, this utility should only be used in one-time use situations,
+ * such as a 'for-each' loop.
+ */
+ public static class SingleUseIterable<E> implements Iterable<E> {
+ private Iterator<E> iterator;
+
+ public SingleUseIterable(Iterator<? extends E> iterator) {
+ super();
+ if (iterator == null) {
+ throw new NullPointerException();
+ }
+ this.iterator = new SuperIteratorWrapper<E>(iterator);
+ }
+
+ public Iterator<E> iterator() {
+ if (this.iterator == null) {
+ throw new IllegalStateException("This method has already been called."); //$NON-NLS-1$
+ }
+ Iterator<E> result = this.iterator;
+ this.iterator = null;
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return StringTools.buildToStringFor(this, this.iterator);
+ }
+
+ }
+
+
+ // ********** java.util.Collections enhancements **********
+
+ /**
+ * Return the destination list after the source list has been copied into it.
+ * @see Collections#copy(List, List)
+ */
+ public static <E> List<E> copy(List<E> dest, List<? extends E> src) {
+ Collections.copy(dest, src);
+ return dest;
+ }
+
+ /**
+ * Return the list after it has been "filled".
+ * @see Collections#fill(List, Object)
+ */
+ public static <E> List<E> fill(List<E> list, E value) {
+ Collections.fill(list, value);
+ return list;
+ }
+
+ /**
+ * Return the list after it has been "reversed".
+ * @see Collections#reverse(List)
+ */
+ public static <E> List<E> reverse(List<E> list) {
+ Collections.reverse(list);
+ return list;
+ }
+
+ /**
+ * Return the list after it has been "rotated".
+ * @see Collections#rotate(List, int)
+ */
+ public static <E> List<E> rotate(List<E> list, int distance) {
+ Collections.rotate(list, distance);
+ return list;
+ }
+
+ /**
+ * Return the list after it has been "shuffled".
+ * @see Collections#shuffle(List)
+ */
+ public static <E> List<E> shuffle(List<E> list) {
+ Collections.shuffle(list);
+ return list;
+ }
+
+ /**
+ * Return the list after it has been "shuffled".
+ * @see Collections#shuffle(List, Random)
+ */
+ public static <E> List<E> shuffle(List<E> list, Random random) {
+ Collections.shuffle(list, random);
+ return list;
+ }
+
+ /**
+ * Return the list after it has been "sorted".
+ * NB: The list is sorted in place as a side-effect.
+ * @see Collections#sort(List)
+ */
+ public static <E extends Comparable<? super E>> List<E> sort(List<E> list) {
+ Collections.sort(list);
+ return list;
+ }
+
+ /**
+ * Return the list after it has been "sorted".
+ * NB: The list is sorted in place as a side-effect.
+ * @see Collections#sort(List, Comparator)
+ */
+ public static <E> List<E> sort(List<E> list, Comparator<? super E> comparator) {
+ Collections.sort(list, comparator);
+ return list;
+ }
+
+ /**
+ * Return the list after the specified elements have been "swapped".
+ * @see Collections#swap(List, int, int)
+ */
+ public static <E> List<E> swap(List<E> list, int i, int j) {
+ Collections.swap(list, i, j);
+ return list;
+ }
+
+
+ // ********** constructor **********
+
+ /**
+ * Suppress default constructor, ensuring non-instantiability.
+ */
+ private CollectionTools() {
+ super();
+ throw new UnsupportedOperationException();
+ }
+
+}
diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/CommandRunnable.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/CommandRunnable.java
new file mode 100644
index 0000000000..8f66ef255c
--- /dev/null
+++ b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/CommandRunnable.java
@@ -0,0 +1,37 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Oracle. 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:
+ * Oracle - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.jpt.common.utility.internal;
+
+import org.eclipse.jpt.common.utility.Command;
+
+/**
+ * Wrap a Command so it can be used as a Runnable.
+ */
+public class CommandRunnable implements Runnable {
+ protected final Command command;
+
+ public CommandRunnable(Command command) {
+ super();
+ if (command == null) {
+ throw new NullPointerException();
+ }
+ this.command = command;
+ }
+
+ public void run() {
+ this.command.execute();
+ }
+
+ @Override
+ public String toString() {
+ return "Runnable[" + this.command.toString() +']'; //$NON-NLS-1$
+ }
+
+}
diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/CompositeCommand.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/CompositeCommand.java
new file mode 100644
index 0000000000..fce6610235
--- /dev/null
+++ b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/CompositeCommand.java
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Oracle. 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:
+ * Oracle - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.jpt.common.utility.internal;
+
+import org.eclipse.jpt.common.utility.Command;
+import org.eclipse.jpt.common.utility.internal.iterables.ArrayIterable;
+
+/**
+ * <code>CompositeCommand</code> provides support for treating a collection of
+ * {@link Command}s as a single command.
+ */
+public class CompositeCommand
+ implements Command
+{
+ private final Iterable<Command> commands;
+
+ public CompositeCommand(Command... commands) {
+ this(new ArrayIterable<Command>(commands));
+ }
+
+ public CompositeCommand(Iterable<Command> commands) {
+ super();
+ this.commands = commands;
+ }
+
+ public void execute() {
+ for (Command command : this.commands) {
+ command.execute();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return StringTools.buildToStringFor(this, this.commands);
+ }
+
+}
diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/CompositeException.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/CompositeException.java
new file mode 100644
index 0000000000..dcb51283cd
--- /dev/null
+++ b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/CompositeException.java
@@ -0,0 +1,96 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Oracle. 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:
+ * Oracle - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.jpt.common.utility.internal;
+
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.util.Collection;
+
+/**
+ * Provide a way for multiple exceptions to be packaged and reported.
+ */
+public class CompositeException
+ extends RuntimeException
+{
+ private final Throwable[] exceptions;
+ private static final long serialVersionUID = 1L;
+
+
+ /**
+ * The specified exceptions list must not be empty.
+ */
+ public CompositeException(Collection<Throwable> exceptions) {
+ this(exceptions.toArray(new Throwable[exceptions.size()]));
+ }
+
+ /**
+ * The specified exceptions list must not be empty.
+ */
+ public CompositeException(Throwable[] exceptions) {
+ // provide a list of the nested exceptions and
+ // grab the first exception and make it the "cause"
+ super(buildMessage(exceptions));
+ this.exceptions = exceptions;
+ }
+
+ public Throwable[] getExceptions() {
+ return this.exceptions;
+ }
+
+ private static String buildMessage(Throwable[] exceptions) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(exceptions.length);
+ sb.append(" exceptions: "); //$NON-NLS-1$
+ sb.append('[');
+ for (Throwable ex : exceptions) {
+ sb.append(ex.getClass().getSimpleName());
+ sb.append(", "); //$NON-NLS-1$
+ }
+ sb.setLength(sb.length() - 2); // chop off trailing comma
+ sb.append(']');
+ return sb.toString();
+ }
+
+ @Override
+ public void printStackTrace(PrintStream s) {
+ synchronized (s) {
+ s.println(this);
+ for (StackTraceElement element : this.getStackTrace()) {
+ s.print("\tat "); //$NON-NLS-1$
+ s.println(element);
+ }
+ int i = 1;
+ for (Throwable ex : this.exceptions) {
+ s.print("Nested exception "); //$NON-NLS-1$
+ s.print(i++);
+ s.print(": "); //$NON-NLS-1$
+ ex.printStackTrace(s);
+ }
+ }
+ }
+
+ @Override
+ public void printStackTrace(PrintWriter s) {
+ synchronized (s) {
+ s.println(this);
+ for (StackTraceElement element : this.getStackTrace()) {
+ s.print("\tat "); //$NON-NLS-1$
+ s.println(element);
+ }
+ int i = 1;
+ for (Throwable ex : this.exceptions) {
+ s.print("Nested exception "); //$NON-NLS-1$
+ s.print(i++);
+ s.print(": "); //$NON-NLS-1$
+ ex.printStackTrace(s);
+ }
+ }
+ }
+}
diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/ConsumerThreadCoordinator.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/ConsumerThreadCoordinator.java
new file mode 100644
index 0000000000..8dc34e968d
--- /dev/null
+++ b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/ConsumerThreadCoordinator.java
@@ -0,0 +1,253 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Oracle. 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:
+ * Oracle - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.jpt.common.utility.internal;
+
+import java.util.Vector;
+import java.util.concurrent.ThreadFactory;
+
+/**
+ * A <code>ConsumerThreadCoordinator</code> controls the creation,
+ * starting, and stopping of a general purpose "consumer" thread. Construct
+ * the coordinator with a {@link Consumer} that both waits for the producer
+ * to "produce" something to "consume" and, once the wait is over,
+ * "consumes" whatever is available.
+ * <p>
+ * <strong>NB:</strong> The client-supplied consumer should handle any
+ * exception appropriately (e.g. log the exception and return gracefully) so
+ * the thread can continue executing.
+ */
+public class ConsumerThreadCoordinator {
+ /**
+ * The runnable passed to the consumer thread each time it is built.
+ */
+ private final Runnable runnable;
+
+ /**
+ * Client-supplied thread factory. Defaults to a straightforward
+ * implementation.
+ */
+ private final ThreadFactory threadFactory;
+
+ /**
+ * Optional, client-supplied name for the consumer thread.
+ * If null, the JDK assigns a name.
+ */
+ private final String threadName;
+
+ /**
+ * The consumer is executed on this thread. A new thread is built
+ * for every start/stop cycle (since a thread cannot be started more than
+ * once).
+ */
+ private volatile Thread thread;
+
+ /**
+ * A list of the uncaught exceptions thrown by the consumer
+ * during the current start/stop cycle.
+ */
+ final Vector<Throwable> exceptions = new Vector<Throwable>();
+
+
+ // ********** construction **********
+
+ /**
+ * Construct a consumer thread coordinator for the specified consumer.
+ * Use simple JDK thread(s) for the consumer thread(s).
+ * Allow the consumer thread(s) to be assigned JDK-generated names.
+ */
+ public ConsumerThreadCoordinator(Consumer consumer) {
+ this(consumer, SimpleThreadFactory.instance());
+ }
+
+ /**
+ * Construct a consumer thread coordinator for the specified consumer.
+ * Use the specified thread factory to construct the consumer thread(s).
+ * Allow the consumer thread(s) to be assigned JDK-generated names.
+ */
+ public ConsumerThreadCoordinator(Consumer consumer, ThreadFactory threadFactory) {
+ this(consumer, threadFactory, null);
+ }
+
+ /**
+ * Construct a consumer thread coordinator for the specified consumer.
+ * Assign the consumer thread(s) the specified name.
+ * Use simple JDK thread(s) for the consumer thread(s).
+ */
+ public ConsumerThreadCoordinator(Consumer consumer, String threadName) {
+ this(consumer, SimpleThreadFactory.instance(), threadName);
+ }
+
+ /**
+ * Construct a consumer thread coordinator for the specified consumer.
+ * Use the specified thread factory to construct the consumer thread(s).
+ * Assign the consumer thread(s) the specified name.
+ */
+ public ConsumerThreadCoordinator(Consumer consumer, ThreadFactory threadFactory, String threadName) {
+ super();
+ this.runnable = this.buildRunnable(consumer);
+ this.threadFactory = threadFactory;
+ this.threadName = threadName;
+ }
+
+ private Runnable buildRunnable(Consumer consumer) {
+ return new RunnableConsumer(consumer);
+ }
+
+
+ // ********** Lifecycle support **********
+
+ /**
+ * Build and start the consumer thread.
+ */
+ public synchronized void start() {
+ if (this.thread != null) {
+ throw new IllegalStateException("Not stopped."); //$NON-NLS-1$
+ }
+ this.thread = this.buildThread();
+ this.thread.start();
+ }
+
+ private Thread buildThread() {
+ Thread t = this.threadFactory.newThread(this.runnable);
+ if (this.threadName != null) {
+ t.setName(this.threadName);
+ }
+ return t;
+ }
+
+ /**
+ * Interrupt the consumer thread so that it stops executing at the
+ * end of its current iteration. Suspend the current thread until
+ * the consumer thread is finished executing. If any uncaught
+ * exceptions were thrown while the consumer thread was executing,
+ * wrap them in a composite exception and throw the composite exception.
+ */
+ public synchronized void stop() {
+ if (this.thread == null) {
+ throw new IllegalStateException("Not started."); //$NON-NLS-1$
+ }
+ this.thread.interrupt();
+ try {
+ this.thread.join();
+ } catch (InterruptedException ex) {
+ // the thread that called #stop() was interrupted while waiting to
+ // join the consumer thread - ignore;
+ // 'thread' is still "interrupted", so its #run() loop will still stop
+ // after its current execution - we just won't wait around for it...
+ }
+ this.thread = null;
+
+ if (this.exceptions.size() > 0) {
+ Throwable[] temp = this.exceptions.toArray(new Throwable[this.exceptions.size()]);
+ this.exceptions.clear();
+ throw new CompositeException(temp);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return StringTools.buildToStringFor(this, this.thread);
+ }
+
+
+ // ********** consumer thread runnable **********
+
+ /**
+ * This implementation of {@link Runnable} is a long-running consumer that
+ * will repeatedly execute the consumer {@link Consumer#execute()} method.
+ * With each iteration, the consumer thread will wait
+ * until the other consumer method, {@link Consumer#waitForProducer()}, allows the
+ * consumer thread to proceed (i.e. there is something for the consumer to
+ * consume). Once {@link Consumer#execute()} is finished, the thread will quiesce
+ * until {@link Consumer#waitForProducer()} returns again.
+ * Stop the thread by calling {@link Thread#interrupt()}.
+ */
+ private class RunnableConsumer
+ implements Runnable
+ {
+ /**
+ * Client-supplied consumer that controls waiting for something to consume
+ * and the consuming itself.
+ */
+ private final Consumer consumer;
+
+ RunnableConsumer(Consumer consumer) {
+ super();
+ this.consumer = consumer;
+ }
+
+ /**
+ * Loop while this thread has not been interrupted by another thread.
+ * In each loop: Pause execution until {@link Consumer#waitForProducer()}
+ * allows us to proceed.
+ * <p>
+ * If this thread is interrupted <em>during</em> {@link Consumer#execute()},
+ * the call to {@link Thread#interrupted()} will stop the loop. If this thread is
+ * interrupted during the call to {@link Consumer#waitForProducer()},
+ * we will catch the {@link InterruptedException} and stop the loop also.
+ */
+ public void run() {
+ while ( ! Thread.interrupted()) {
+ try {
+ this.consumer.waitForProducer();
+ } catch (InterruptedException ex) {
+ // we were interrupted while waiting, must be Quittin' Time
+ return;
+ }
+ this.execute();
+ }
+ }
+
+ /**
+ * Execute the consumer {@link Consumer#execute()} method.
+ * Do not allow any unhandled exceptions to kill the thread.
+ * Store them up for later pain.
+ * @see ConsumerThreadCoordinator#stop()
+ */
+ private void execute() {
+ try {
+ this.execute_();
+ } catch (Throwable ex) {
+ ConsumerThreadCoordinator.this.exceptions.add(ex);
+ }
+ }
+
+ /**
+ * Subclass-implemented behavior: consume stuff.
+ */
+ private void execute_() {
+ this.consumer.execute();
+ }
+
+ }
+
+
+ // ********** consumer interface **********
+
+ /**
+ * Interface implemented by clients that controls:<ul>
+ * <li>when the consumer thread suspends, waiting for something to consume
+ * <li>the consuming of whatever is being produced
+ * </ul>
+ */
+ public interface Consumer {
+ /**
+ * Wait for something to consume.
+ * Throw an {@link InterruptedException} if the thread is interrupted.
+ */
+ void waitForProducer() throws InterruptedException;
+
+ /**
+ * Consume whatever is currently available.
+ */
+ void execute();
+ }
+
+}
diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/ExceptionHandler.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/ExceptionHandler.java
new file mode 100644
index 0000000000..9c32f555a9
--- /dev/null
+++ b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/ExceptionHandler.java
@@ -0,0 +1,87 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 Oracle. 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:
+ * Oracle - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.jpt.common.utility.internal;
+
+import java.io.Serializable;
+
+/**
+ * Simple interface for allowing clients to pass an exception handler to a
+ * service (e.g. to log the exception). This is particularly helpful if the
+ * service executes on another, possibly inaccessible, thread.
+ * <p>
+ * Provisional API: This interface is part of an interim API that is still
+ * under development and expected to change significantly before reaching
+ * stability. It is available at this early stage to solicit feedback from
+ * pioneering adopters on the understanding that any code that uses this API
+ * will almost certainly be broken (repeatedly) as the API evolves.
+ */
+public interface ExceptionHandler {
+
+ /**
+ * The specified exception was thrown. Handle it appropriately.
+ */
+ void handleException(Throwable t);
+
+ /**
+ * Singleton implementation of the exception handler interface that does
+ * nothing with the exception.
+ */
+ final class Null implements ExceptionHandler, Serializable {
+ public static final ExceptionHandler INSTANCE = new Null();
+ public static ExceptionHandler instance() {
+ return INSTANCE;
+ }
+ // ensure single instance
+ private Null() {
+ super();
+ }
+ public void handleException(Throwable t) {
+ // do nothing
+ }
+ @Override
+ public String toString() {
+ return "ExceptionHandler.Null"; //$NON-NLS-1$
+ }
+ private static final long serialVersionUID = 1L;
+ private Object readResolve() {
+ // replace this object with the singleton
+ return INSTANCE;
+ }
+ }
+
+ /**
+ * Singleton implementation of the exception handler interface that
+ * wraps the exception in a runtime exception and throws it.
+ */
+ final class Runtime implements ExceptionHandler, Serializable {
+ public static final ExceptionHandler INSTANCE = new Runtime();
+ public static ExceptionHandler instance() {
+ return INSTANCE;
+ }
+ // ensure single instance
+ private Runtime() {
+ super();
+ }
+ public void handleException(Throwable t) {
+ // re-throw the exception unchecked
+ throw new RuntimeException(t);
+ }
+ @Override
+ public String toString() {
+ return "ExceptionHandler.Runtime"; //$NON-NLS-1$
+ }
+ private static final long serialVersionUID = 1L;
+ private Object readResolve() {
+ // replace this object with the singleton
+ return INSTANCE;
+ }
+ }
+
+}
diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/FileTools.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/FileTools.java
new file mode 100644
index 0000000000..cfe93a64b7
--- /dev/null
+++ b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/FileTools.java
@@ -0,0 +1,1002 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2010 Oracle. 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:
+ * Oracle - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.jpt.common.utility.internal;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.nio.channels.FileChannel;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Set;
+
+import org.eclipse.jpt.common.utility.internal.iterators.ArrayIterator;
+import org.eclipse.jpt.common.utility.internal.iterators.CompositeIterator;
+import org.eclipse.jpt.common.utility.internal.iterators.FilteringIterator;
+import org.eclipse.jpt.common.utility.internal.iterators.TransformationIterator;
+
+/**
+ * Assorted file tools:
+ * - delete entire trees of directories and files
+ * - build iterators on entire trees of directories and files
+ * - build a temporary directory
+ * - "canonize" files
+ */
+public final class FileTools {
+
+ public static final String USER_HOME_DIRECTORY_NAME = System.getProperty("user.home"); //$NON-NLS-1$
+ public static final String USER_TEMPORARY_DIRECTORY_NAME = System.getProperty("java.io.tmpdir"); //$NON-NLS-1$
+ public static String DEFAULT_TEMPORARY_DIRECTORY_NAME = "tmpdir"; //$NON-NLS-1$
+ public static final String CURRENT_WORKING_DIRECTORY_NAME = System.getProperty("user.dir"); //$NON-NLS-1$
+
+ /** A list of some invalid file name characters.
+ : is the filename separator in MacOS and the drive indicator in DOS
+ * is a DOS wildcard character
+ | is a DOS redirection character
+ & is our own escape character
+ / is the filename separator in Unix and the command option tag in DOS
+ \ is the filename separator in DOS/Windows and the escape character in Unix
+ ; is ???
+ ? is a DOS wildcard character
+ [ is ???
+ ] is ???
+ = is ???
+ + is ???
+ < is a DOS redirection character
+ > is a DOS redirection character
+ " is used by DOS to delimit file names with spaces
+ , is ???
+ */
+ public static final char[] INVALID_FILENAME_CHARACTERS = { ':', '*', '|', '&', '/', '\\', ';', '?', '[', ']', '=', '+', '<', '>', '"', ',' };
+
+ /** This encoder will convert strings into valid file names. */
+ public static final XMLStringEncoder FILE_NAME_ENCODER = new XMLStringEncoder(INVALID_FILENAME_CHARACTERS);
+
+ /** Windows files that are redirected to devices etc. */
+ @SuppressWarnings("nls")
+ private static final String[] WINDOWS_RESERVED_FILE_NAMES = {
+ "con",
+ "aux",
+ "com1", "com2", "com3", "com4", "com5", "com6", "com7", "com8", "com9",
+ "lpt1", "lpt2", "lpt3", "lpt4", "lpt5", "lpt6", "lpt7", "lpt8", "lpt9",
+ "prn",
+ "nul"
+ };
+
+ /** The default length of a shortened file name. */
+ public static final int MAXIMUM_SHORTENED_FILE_NAME_LENGTH = 60;
+
+
+ // ********** deleting directories **********
+
+ /**
+ * Delete the specified directory and all of its contents.
+ * <em>USE WITH CARE.</em>
+ * File#deleteAll()?
+ */
+ public static void deleteDirectory(String directoryName) {
+ deleteDirectory(new File(directoryName));
+ }
+
+ /**
+ * Delete the specified directory and all of its contents.
+ * <em>USE WITH CARE.</em>
+ * File#deleteAll()?
+ */
+ public static void deleteDirectory(File directory) {
+ deleteDirectoryContents(directory);
+ if ( ! directory.delete()) {
+ throw new RuntimeException("unable to delete directory: " + directory.getAbsolutePath()); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * Delete the contents of the specified directory
+ * (but not the directory itself).
+ * <em>USE WITH CARE.</em>
+ * File#deleteFiles()
+ */
+ public static void deleteDirectoryContents(String directoryName) {
+ deleteDirectoryContents(new File(directoryName));
+ }
+
+ /**
+ * Delete the contents of the specified directory
+ * (but not the directory itself).
+ * <em>USE WITH CARE.</em>
+ * File#deleteFiles()
+ */
+ public static void deleteDirectoryContents(File directory) {
+ for (File file : directory.listFiles()) {
+ if (file.isDirectory()) {
+ deleteDirectory(file); // recurse through subdirectories
+ } else {
+ if ( ! file.delete()) {
+ throw new RuntimeException("unable to delete file: " + file.getAbsolutePath()); //$NON-NLS-1$
+ }
+ }
+ }
+ }
+
+
+ // ********** copying files **********
+
+ /**
+ * Copies the content of the source file to the destination file.
+ * File#copy(File destinationFile)
+ */
+ public static void copyToFile(File sourceFile, File destinationFile)
+ throws IOException
+ {
+ FileChannel sourceChannel = new FileInputStream(sourceFile).getChannel();
+ FileChannel destinationChannel = new FileOutputStream(destinationFile).getChannel();
+ try {
+ destinationChannel.transferFrom(sourceChannel, 0, sourceChannel.size());
+ } finally {
+ sourceChannel.close();
+ destinationChannel.close();
+ }
+ }
+
+ /**
+ * Copies the content of the source file to a file by
+ * the same name in the destination directory.
+ * File#copyToDirectory(File destinationDirectory)
+ */
+ public static void copyToDirectory(File sourceFile, File destinationDirectory)
+ throws IOException
+ {
+ File destinationFile = new File(destinationDirectory, sourceFile.getName());
+ if ( ! destinationFile.exists() && ! destinationFile.createNewFile()) {
+ throw new RuntimeException("createNewFile() failed: " + destinationFile); //$NON-NLS-1$
+ }
+ copyToFile(sourceFile, destinationFile);
+ }
+
+
+ // ********** iteratoring over files and directories **********
+
+ /**
+ * Return an iterator on all the files in the specified directory.
+ * The iterator will skip over subdirectories.
+ * File#files()
+ */
+ public static Iterator<File> filesIn(String directoryName) {
+ return filesIn(new File(directoryName));
+ }
+
+ /**
+ * Return an iterator on all the files in the specified directory.
+ * The iterator will skip over subdirectories.
+ * File#files()
+ */
+ public static Iterator<File> filesIn(File directory) {
+ return filesIn(directory.listFiles());
+ }
+
+ private static Iterator<File> filesIn(File[] files) {
+ return new FilteringIterator<File>(new ArrayIterator<File>(files)) {
+ @Override
+ protected boolean accept(File next) {
+ return next.isFile();
+ }
+ };
+ }
+
+ /**
+ * Return an iterator on all the subdirectories
+ * in the specified directory.
+ * File#subDirectories()
+ */
+ public static Iterator<File> directoriesIn(String directoryName) {
+ return directoriesIn(new File(directoryName));
+ }
+
+ /**
+ * Return an iterator on all the subdirectories
+ * in the specified directory.
+ * File#subDirectories()
+ */
+ public static Iterator<File> directoriesIn(File directory) {
+ return directoriesIn(directory.listFiles());
+ }
+
+ private static Iterator<File> directoriesIn(File[] files) {
+ return new FilteringIterator<File>(new ArrayIterator<File>(files)) {
+ @Override
+ protected boolean accept(File next) {
+ return next.isDirectory();
+ }
+ };
+ }
+
+ /**
+ * Return an iterator on all the files under the specified
+ * directory, recursing into subdirectories.
+ * The iterator will skip over the subdirectories themselves.
+ * File#filesRecurse()
+ */
+ public static Iterator<File> filesInTree(String directoryName) {
+ return filesInTree(new File(directoryName));
+ }
+
+ /**
+ * Return an iterator on all the files under the specified
+ * directory, recursing into subdirectories.
+ * The iterator will skip over the subdirectories themselves.
+ * File#filesRecurse()
+ */
+ public static Iterator<File> filesInTree(File directory) {
+ return filesInTreeAsSet(directory).iterator();
+ }
+
+ private static Set<File> filesInTreeAsSet(File directory) {
+ Set<File> files = new HashSet<File>(10000);
+ addFilesInTreeTo(directory, files);
+ return files;
+ }
+
+ private static void addFilesInTreeTo(File directory, Collection<File> allFiles) {
+ for (File file : directory.listFiles()) {
+ if (file.isFile()) {
+ allFiles.add(file);
+ } else if (file.isDirectory()) {
+ addFilesInTreeTo(file, allFiles);
+ }
+ }
+ }
+
+ /**
+ * Return an iterator on all the directories under the specified
+ * directory, recursing into subdirectories.
+ * File#subDirectoriesRecurse()
+ */
+ public static Iterator<File> directoriesInTree(String directoryName) {
+ return directoriesInTree(new File(directoryName));
+ }
+
+ /**
+ * Return an iterator on all the directories under the specified
+ * directory, recursing into subdirectories.
+ * File#subDirectoriesRecurse()
+ */
+ @SuppressWarnings("unchecked")
+ public static Iterator<File> directoriesInTree(File directory) {
+ File[] files = directory.listFiles();
+ return new CompositeIterator<File>(directoriesIn(files), directoriesInTrees(directoriesIn(files)));
+ }
+
+ private static Iterator<File> directoriesInTrees(Iterator<File> directories) {
+ return new CompositeIterator<File>(
+ new TransformationIterator<File, Iterator<File>>(directories) {
+ @Override
+ protected Iterator<File> transform(File next) {
+ return FileTools.directoriesInTree(next);
+ }
+ }
+ );
+ }
+
+
+ // ********** short file name manipulation **********
+
+ /**
+ * Strip the extension from the specified file name
+ * and return the result. If the file name has no
+ * extension, it is returned unchanged
+ * File#basePath()
+ */
+ public static String stripExtension(String fileName) {
+ int index = fileName.lastIndexOf('.');
+ if (index == -1) {
+ return fileName;
+ }
+ return fileName.substring(0, index);
+ }
+
+ /**
+ * Strip the extension from the specified file's name
+ * and return the result. If the file's name has no
+ * extension, it is returned unchanged
+ * File#basePath()
+ */
+ public static String stripExtension(File file) {
+ return stripExtension(file.getPath());
+ }
+
+ /**
+ * Return the extension, including the dot, of the specified file name.
+ * If the file name has no extension, return an empty string.
+ * File#extension()
+ */
+ public static String extension(String fileName) {
+ int index = fileName.lastIndexOf('.');
+ if (index == -1) {
+ return ""; //$NON-NLS-1$
+ }
+ return fileName.substring(index);
+ }
+
+ /**
+ * Return the extension, including the dot, of the specified file's name.
+ * If the file's name has no extension, return an empty string.
+ * File#extension()
+ */
+ public static String extension(File file) {
+ return extension(file.getPath());
+ }
+
+
+ // ********** temporary directories **********
+
+ /**
+ * Build and return an empty temporary directory with the specified
+ * name. If the directory already exists, it will be cleared out.
+ * This directory will be a subdirectory of the Java temporary directory,
+ * as indicated by the System property "java.io.tmpdir".
+ */
+ public static File emptyTemporaryDirectory(String name) {
+ File dir = new File(userTemporaryDirectory(), name);
+ if (dir.exists()) {
+ deleteDirectoryContents(dir);
+ } else {
+ mkdirs(dir);
+ }
+ return dir;
+ }
+
+ private static void mkdirs(File dir) {
+ if ( ! dir.mkdirs()) {
+ throw new RuntimeException("mkdirs() failed: " + dir); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * Build and return an empty temporary directory with a
+ * name of "tmpdir". If the directory already exists, it will be cleared out.
+ * This directory will be a subdirectory of the Java temporary directory,
+ * as indicated by the System property "java.io.tmpdir".
+ */
+ public static File emptyTemporaryDirectory() {
+ return emptyTemporaryDirectory(DEFAULT_TEMPORARY_DIRECTORY_NAME);
+ }
+
+ /**
+ * Build and return a temporary directory with the specified
+ * name. If the directory already exists, it will be left unchanged;
+ * if it does not already exist, it will be created.
+ * This directory will be a subdirectory of the Java temporary directory,
+ * as indicated by the System property "java.io.tmpdir".
+ */
+ public static File temporaryDirectory(String name) {
+ File dir = new File(userTemporaryDirectory(), name);
+ if ( ! dir.exists()) {
+ mkdirs(dir);
+ }
+ return dir;
+ }
+
+ /**
+ * Build and return a temporary directory with a name of
+ * "tmpdir". If the directory already exists, it will be left unchanged;
+ * if it does not already exist, it will be created.
+ * This directory will be a subdirectory of the Java temporary directory,
+ * as indicated by the System property "java.io.tmpdir".
+ */
+ public static File temporaryDirectory() {
+ return temporaryDirectory(DEFAULT_TEMPORARY_DIRECTORY_NAME);
+ }
+
+ /**
+ * Build and return a *new* temporary directory with the specified
+ * prefix. The prefix will be appended with a number that
+ * is incremented, starting with 1, until a non-pre-existing directory
+ * is found and successfully created. This directory will be a
+ * subdirectory of the Java temporary directory, as indicated by
+ * the System property "java.io.tmpdir".
+ */
+ public static File newTemporaryDirectory(String prefix) {
+ if ( ! prefix.endsWith(".")) { //$NON-NLS-1$
+ prefix = prefix + '.';
+ }
+ File dir;
+ int i = 0;
+ do {
+ i++;
+ dir = new File(userTemporaryDirectory(), prefix + i);
+ } while ( ! dir.mkdirs());
+ return dir;
+ }
+
+ /**
+ * Build and return a *new* temporary directory with a
+ * prefix of "tmpdir". This prefix will be appended with a number that
+ * is incremented, starting with 1, until a non-pre-existing directory
+ * is found and successfully created. This directory will be a
+ * subdirectory of the Java temporary directory, as indicated by
+ * the System property "java.io.tmpdir".
+ */
+ public static File newTemporaryDirectory() {
+ return newTemporaryDirectory(DEFAULT_TEMPORARY_DIRECTORY_NAME);
+ }
+
+
+ // ********** resource files **********
+
+ /**
+ * Build and return a file for the specified resource.
+ * The resource name must be fully-qualified, i.e. it cannot be relative
+ * to the package name/directory.
+ * NB: There is a bug in jdk1.4.x the prevents us from getting
+ * a resource that has spaces (or other special characters) in
+ * its name.... (see Sun's Java bug 4466485)
+ */
+ public static File resourceFile(String resourceName) throws URISyntaxException {
+ if ( ! resourceName.startsWith("/")) { //$NON-NLS-1$
+ throw new IllegalArgumentException(resourceName);
+ }
+ return resourceFile(resourceName, FileTools.class);
+ }
+
+ /**
+ * Build and return a file for the specified resource.
+ * NB: There is a bug in jdk1.4.x the prevents us from getting
+ * a resource that has spaces (or other special characters) in
+ * its name.... (see Sun's Java bug 4466485)
+ */
+ public static File resourceFile(String resourceName, Class<?> javaClass) throws URISyntaxException {
+ URL url = javaClass.getResource(resourceName);
+ return buildFile(url);
+ }
+
+ /**
+ * Build and return a file for the specified URL.
+ * NB: There is a bug in jdk1.4.x the prevents us from getting
+ * a resource that has spaces (or other special characters) in
+ * its name.... (see Sun's Java bug 4466485)
+ */
+ public static File buildFile(URL url) throws URISyntaxException {
+ return buildFile(url.getFile());
+ }
+
+ /**
+ * Build and return a file for the specified file name.
+ * NB: There is a bug in jdk1.4.x the prevents us from getting
+ * a resource that has spaces (or other special characters) in
+ * its name.... (see Sun's Java bug 4466485)
+ */
+ public static File buildFile(String fileName) throws URISyntaxException {
+ URI uri = new URI(fileName);
+ File file = new File(uri.getPath());
+ return file;
+ }
+
+
+ // ********** "canonical" files **********
+
+ /**
+ * Convert the specified file into a "canonical" file.
+ */
+ public static File canonicalFile(File file) {
+ try {
+ return file.getCanonicalFile();
+ } catch (IOException ioexception) {
+ // settle for the absolute file
+ return file.getAbsoluteFile();
+ }
+ }
+
+ /**
+ * Build an iterator that will convert the specified files
+ * into "canonical" files.
+ */
+ public static Iterator<File> canonicalFiles(Iterator<File> files) {
+ return new TransformationIterator<File, File>(files) {
+ @Override
+ protected File transform(File next) {
+ return canonicalFile(next);
+ }
+ };
+ }
+
+ /**
+ * Build an iterator that will convert the specified files
+ * into "canonical" files.
+ */
+ public static Iterator<File> canonicalFiles(Collection<File> files) {
+ return canonicalFiles(files.iterator());
+ }
+
+ /**
+ * Convert the specified file name into a "canonical" file name.
+ */
+ public static String canonicalFileName(String fileName) {
+ return canonicalFile(new File(fileName)).getAbsolutePath();
+ }
+
+ /**
+ * Build an iterator that will convert the specified file names
+ * into "canonical" file names.
+ */
+ public static Iterator<String> canonicalFileNames(Iterator<String> fileNames) {
+ return new TransformationIterator<String, String>(fileNames) {
+ @Override
+ protected String transform(String next) {
+ return canonicalFileName(next);
+ }
+ };
+ }
+
+ /**
+ * Build an iterator that will convert the specified file names
+ * into "canonical" file names.
+ */
+ public static Iterator<String> canonicalFileNames(Collection<String> fileNames) {
+ return canonicalFileNames(fileNames.iterator());
+ }
+
+
+ // ********** file name validation **********
+
+ /**
+ * Return whether the specified file name is invalid.
+ */
+ public static boolean fileNameIsInvalid(String filename) {
+ return ! fileNameIsValid(filename);
+ }
+
+ /**
+ * Return whether the specified file name is valid.
+ */
+ public static boolean fileNameIsValid(String filename) {
+ int len = filename.length();
+ for (int i = 0; i < len; i++) {
+ char filenameChar = filename.charAt(i);
+ if (ArrayTools.contains(INVALID_FILENAME_CHARACTERS, filenameChar)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Convert the illegal characters in the specified file name to
+ * the specified character and return the result.
+ */
+ public static String convertToValidFileName(String filename, char replacementChar) {
+ int len = filename.length();
+ StringBuilder sb = new StringBuilder(len);
+ for (int i = 0; i < len; i++) {
+ char filenameChar = filename.charAt(i);
+ if (ArrayTools.contains(INVALID_FILENAME_CHARACTERS, filenameChar)) {
+ sb.append(replacementChar);
+ } else {
+ sb.append(filenameChar);
+ }
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Convert the illegal characters in the specified file name to
+ * periods ('.') and return the result.
+ */
+ public static String convertToValidFileName(String filename) {
+ return convertToValidFileName(filename, '.');
+ }
+
+ /**
+ * Return whether the specified file name is "reserved"
+ * (i.e. it cannot be used for "user" files). Windows reserves
+ * a number of file names (e.g. CON, AUX, PRN).
+ */
+ public static boolean fileNameIsReserved(String fileName) {
+ // Unix/Linux does not have any "reserved" file names (I think...)
+ return Tools.osIsWindows() && ArrayTools.contains(WINDOWS_RESERVED_FILE_NAMES, fileName.toLowerCase());
+ }
+
+ /**
+ * Return whether the specified file contains any "reserved"
+ * components.
+ * Windows reserves a number of file names (e.g. CON, AUX, PRN);
+ * and these file names cannot be used for either the names of
+ * files or directories.
+ */
+ public static boolean fileHasAnyReservedComponents(File file) {
+ File temp = file;
+ while (temp != null) {
+ if (fileNameIsReserved(temp.getName())) {
+ return true;
+ }
+ temp = temp.getParentFile();
+ }
+ return false;
+ }
+
+
+ // ********** shortened file names **********
+
+ /**
+ * Return a shorter version of the absolute file name for the specified file.
+ * The shorter version will not be longer than the maximum length.
+ * The first directory (usually the drive letter) and the file name or the
+ * last directory will always be added to the generated string regardless of
+ * the maximum length allowed.
+ */
+ public static String shortenFileName(URL url) {
+ return shortenFileName(url, MAXIMUM_SHORTENED_FILE_NAME_LENGTH);
+ }
+
+ /**
+ * Return a shorter version of the absolute file name for the specified file.
+ * The shorter version will not be longer than the maximum length.
+ * The first directory (usually the drive letter) and the file name or the
+ * last directory will always be added to the generated string regardless of
+ * the maximum length allowed.
+ */
+ public static String shortenFileName(URL url, int maxLength) {
+ File file;
+ try {
+ file = buildFile(url);
+ } catch (URISyntaxException e) {
+ file = new File(url.getFile());
+ }
+ return shortenFileName(file, maxLength);
+ }
+
+ /**
+ * Return a shorter version of the absolute file name for the specified file.
+ * The shorter version will not be longer than the maximum length.
+ * The first directory (usually the drive letter) and the file name or the
+ * last directory will always be added to the generated string regardless of
+ * the maximum length allowed.
+ */
+ public static String shortenFileName(File file) {
+ return shortenFileName(file, MAXIMUM_SHORTENED_FILE_NAME_LENGTH);
+ }
+
+ /**
+ * Return a shorter version of the absolute file name for the specified file.
+ * The shorter version will not be longer than the maximum length.
+ * The first directory (usually the drive letter) and the file name or the
+ * last directory will always be added to the generated string regardless of
+ * the maximum length allowed.
+ */
+ public static String shortenFileName(File file, int maxLength) {
+ String absoluteFileName = canonicalFile(file).getAbsolutePath();
+ if (absoluteFileName.length() <= maxLength) {
+ // no need to shorten
+ return absoluteFileName;
+ }
+
+ // break down the path into its components
+ String fs = File.separator;
+ String[] paths = absoluteFileName.split('\\' + fs);
+
+ if (paths.length <= 1) {
+ // e.g. "C:\"
+ return paths[0];
+ }
+
+ if (paths.length == 2) {
+ // e.g. "C:\MyReallyLongFileName.ext" or "C:\MyReallyLongDirectoryName"
+ // return the complete file name since this is a minimum requirement,
+ // regardless of the maximum length allowed
+ return absoluteFileName;
+ }
+
+ StringBuilder sb = new StringBuilder();
+ sb.append(paths[0]); // always add the first directory, which is usually the drive letter
+
+ // Keep the index of insertion into the string buffer
+ int insertIndex = sb.length();
+
+ sb.append(fs);
+ sb.append(paths[paths.length - 1]); // append the file name or the last directory
+
+ maxLength -= 4; // -4 for "/..."
+
+ int currentLength = sb.length() - 4; // -4 for "/..."
+ int leftIndex = 1; // 1 to skip the root directory
+ int rightIndex = paths.length - 2; // -1 for the file name or the last directory
+
+ boolean canAddFromLeft = true;
+ boolean canAddFromRight = true;
+
+ // Add each directory, the insertion is going in both direction: left and
+ // right, once a side can't be added, the other side is still continuing
+ // until both can't add anymore
+ while (true) {
+ if (!canAddFromLeft && !canAddFromRight)
+ break;
+
+ if (canAddFromRight) {
+ String rightDirectory = paths[rightIndex];
+ int rightLength = rightDirectory.length();
+
+ // Add the directory on the right side of the loop
+ if (currentLength + rightLength + 1 <= maxLength) {
+ sb.insert(insertIndex, fs);
+ sb.insert(insertIndex + 1, rightDirectory);
+
+ currentLength += rightLength + 1;
+ rightIndex--;
+
+ // The right side is now overlapping the left side, that means
+ // we can't add from the right side anymore
+ if (leftIndex >= rightIndex) {
+ canAddFromRight = false;
+ }
+ } else {
+ canAddFromRight = false;
+ }
+ }
+
+ if (canAddFromLeft) {
+ String leftDirectory = paths[leftIndex];
+ int leftLength = leftDirectory.length();
+
+ // Add the directory on the left side of the loop
+ if (currentLength + leftLength + 1 <= maxLength) {
+ sb.insert(insertIndex, fs);
+ sb.insert(insertIndex + 1, leftDirectory);
+
+ insertIndex += leftLength + 1;
+ currentLength += leftLength + 1;
+ leftIndex++;
+
+ // The left side is now overlapping the right side, that means
+ // we can't add from the left side anymore
+ if (leftIndex >= rightIndex) {
+ canAddFromLeft = false;
+ }
+ } else {
+ canAddFromLeft = false;
+ }
+ }
+ }
+
+ if (leftIndex <= rightIndex) {
+ sb.insert(insertIndex, fs);
+ sb.insert(insertIndex + 1, "..."); //$NON-NLS-1$
+ }
+
+ return sb.toString();
+ }
+
+
+ // ********** system properties **********
+
+ /**
+ * Return a file representing the user's home directory.
+ */
+ public static File userHomeDirectory() {
+ return new File(USER_HOME_DIRECTORY_NAME);
+ }
+
+ /**
+ * Return a file representing the user's temporary directory.
+ */
+ public static File userTemporaryDirectory() {
+ return new File(USER_TEMPORARY_DIRECTORY_NAME);
+ }
+
+ /**
+ * Return a file representing the current working directory.
+ */
+ public static File currentWorkingDirectory() {
+ return new File(CURRENT_WORKING_DIRECTORY_NAME);
+ }
+
+
+ // ********** miscellaneous **********
+
+ /**
+ * Return only the files that fit the filter.
+ * File#files(FileFilter fileFilter)
+ */
+ public static Iterator<File> filter(Iterator<File> files, final FileFilter fileFilter) {
+ return new FilteringIterator<File>(files) {
+ @Override
+ protected boolean accept(File next) {
+ return fileFilter.accept(next);
+ }
+ };
+ }
+
+ /**
+ * Return a file that is a re-specification of the specified
+ * file, relative to the specified directory.
+ * Linux/Unix/Mac:
+ * convertToRelativeFile(/foo/bar/baz.java, /foo)
+ * => bar/baz.java
+ * Windows:
+ * convertToRelativeFile(C:\foo\bar\baz.java, C:\foo)
+ * => bar/baz.java
+ * The file can be either a file or a directory; the directory
+ * *should* be a directory.
+ * If the file is already relative or it cannot be made relative
+ * to the directory, it will be returned unchanged.
+ *
+ * NB: This method has been tested on Windows and Linux,
+ * but not Mac (but the Mac is Unix-based these days, so
+ * it shouldn't be a problem...).
+ */
+ public static File convertToRelativeFile(final File file, final File dir) {
+ // check whether the file is already relative
+ if ( ! file.isAbsolute()) {
+ return file; // return unchanged
+ }
+
+ File cFile = canonicalFile(file);
+ File cDir = canonicalFile(dir);
+
+ // the two are the same directory
+ if (cFile.equals(cDir)) {
+ return new File("."); //$NON-NLS-1$
+ }
+
+ File[] filePathFiles = pathFiles(cFile);
+ File[] dirPathFiles = pathFiles(cDir);
+
+ // Windows only (?): the roots are different - e.g. D:\ vs. C:\
+ if ( ! dirPathFiles[0].equals(filePathFiles[0])) {
+ return file; // return unchanged
+ }
+
+ // at this point we know the root is the same, now find how much is in common
+ int i = 0; // this will point at the first miscompare
+ while ((i < dirPathFiles.length) && (i < filePathFiles.length)) {
+ if (dirPathFiles[i].equals(filePathFiles[i])) {
+ i++;
+ } else {
+ break;
+ }
+ }
+ // save our current position
+ int firstMismatch = i;
+
+ // check whether the file is ABOVE the directory: ../..
+ if (firstMismatch == filePathFiles.length) {
+ return relativeParentFile(dirPathFiles.length - firstMismatch);
+ }
+
+ // build a new file from the path beyond the matching portions
+ File diff = new File(filePathFiles[i].getName());
+ while (++i < filePathFiles.length) {
+ diff = new File(diff, filePathFiles[i].getName());
+ }
+
+ // check whether the file is BELOW the directory: subdir1/subdir2/file.ext
+ if (firstMismatch == dirPathFiles.length) {
+ return diff;
+ }
+
+ // the file must be a PEER of the directory: ../../subdir1/subdir2/file.ext
+ return new File(relativeParentFile(dirPathFiles.length - firstMismatch), diff.getPath());
+ }
+
+ /**
+ * Return a file that is a re-specification of the specified
+ * file, relative to the current working directory.
+ * Linux/Unix/Mac (CWD = /foo):
+ * convertToRelativeFile(/foo/bar/baz.java)
+ * => bar/baz.java
+ * Windows (CWD = C:\foo):
+ * convertToRelativeFile(C:\foo\bar\baz.java)
+ * => bar/baz.java
+ * The file can be either a file or a directory.
+ * If the file is already relative or it cannot be made relative
+ * to the directory, it will be returned unchanged.
+ *
+ * NB: This method has been tested on Windows and Linux,
+ * but not Mac (but the Mac is Unix-based these days, so
+ * it shouldn't be a problem...).
+ */
+ public static File convertToRelativeFile(final File file) {
+ return convertToRelativeFile(file, currentWorkingDirectory());
+ }
+
+ /**
+ * Return an array of files representing the path to the specified
+ * file. For example:
+ * C:/foo/bar/baz.txt =>
+ * { C:/, C:/foo, C:/foo/bar, C:/foo/bar/baz.txt }
+ */
+ private static File[] pathFiles(File file) {
+ List<File> path = new ArrayList<File>();
+ for (File f = file; f != null; f = f.getParentFile()) {
+ path.add(f);
+ }
+ Collections.reverse(path);
+ return path.toArray(new File[path.size()]);
+ }
+
+ /**
+ * Return a file with the specified (non-zero) number of relative
+ * file names, e.g. xxx(3) => ../../..
+ */
+ private static File relativeParentFile(int len) {
+ if (len <= 0) {
+ throw new IllegalArgumentException("length must be greater than zero: " + len); //$NON-NLS-1$
+ }
+ File result = new File(".."); //$NON-NLS-1$
+ for (int i = len - 1; i-- > 0; ) {
+ result = new File(result, ".."); //$NON-NLS-1$
+ }
+ return result;
+ }
+
+ /**
+ * Return a file that is a re-specification of the specified
+ * file, absolute to the specified directory.
+ * Linux/Unix/Mac:
+ * convertToAbsoluteFile(bar/baz.java, /foo)
+ * => /foo/bar/baz.java
+ * Windows:
+ * convertToAbsoluteFile(bar/baz.java, C:\foo)
+ * => C:\foo\bar\baz.java
+ * The file can be either a file or a directory; the directory
+ * *should* be a directory.
+ * If the file is already absolute, it will be returned unchanged.
+ *
+ * NB: This method has been tested on Windows and Linux,
+ * but not Mac (but the Mac is Unix-based these days, so
+ * it shouldn't be a problem...).
+ */
+ public static File convertToAbsoluteFile(final File file, final File dir) {
+ // check whether the file is already absolute
+ if (file.isAbsolute()) {
+ return file; // return unchanged
+ }
+ return canonicalFile(new File(dir, file.getPath()));
+ }
+
+ /**
+ * Return a file that is a re-specification of the specified
+ * file, absolute to the current working directory.
+ * Linux/Unix/Mac (CWD = /foo):
+ * convertToAbsoluteFile(bar/baz.java)
+ * => /foo/bar/baz.java
+ * Windows (CWD = C:\foo):
+ * convertToAbsoluteFile(bar/baz.java)
+ * => C:\foo\bar\baz.java
+ * The file can be either a file or a directory.
+ * If the file is already absolute, it will be returned unchanged.
+ *
+ * NB: This method has been tested on Windows and Linux,
+ * but not Mac (but the Mac is Unix-based these days, so
+ * it shouldn't be a problem...).
+ */
+ public static File convertToAbsoluteFile(final File file) {
+ return convertToAbsoluteFile(file, currentWorkingDirectory());
+ }
+
+
+ // ********** constructor **********
+
+ /**
+ * Suppress default constructor, ensuring non-instantiability.
+ */
+ private FileTools() {
+ super();
+ throw new UnsupportedOperationException();
+ }
+
+}
diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/FlaggedObjectReference.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/FlaggedObjectReference.java
new file mode 100644
index 0000000000..84d06c6552
--- /dev/null
+++ b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/FlaggedObjectReference.java
@@ -0,0 +1,69 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Oracle. 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:
+ * Oracle - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.jpt.common.utility.internal;
+
+/**
+ * Provide a container for passing an object that can be changed by the
+ * recipient. If the value is set at any time after construction of the
+ * reference, the reference is marked "set". This allows the client to
+ * detect whether the server/recipient ever set the value, even if it remains
+ * unchanged. This is particularly useful when the value can be set to
+ * <code>null</code>.
+ * <p>
+ * The reference can be set multiple times, but it can
+ * never be "unset" once it is "set".
+ */
+public class FlaggedObjectReference<V>
+ extends SimpleObjectReference<V>
+{
+ private volatile boolean set = false;
+
+ private static final long serialVersionUID = 1L;
+
+
+ // ********** constructors **********
+
+ /**
+ * Create an object reference with the specified initial value.
+ */
+ public FlaggedObjectReference(V value) {
+ super(value);
+ }
+
+ /**
+ * Create an object reference with an initial value of
+ * <code>null</code>.
+ */
+ public FlaggedObjectReference() {
+ super();
+ }
+
+
+ // ********** set **********
+
+ public boolean isSet() {
+ return this.set;
+ }
+
+
+ // ********** overrides **********
+
+ @Override
+ public V setValue(V value) {
+ this.set = true;
+ return super.setValue(value);
+ }
+
+ @Override
+ public String toString() {
+ String s = super.toString();
+ return (this.set) ? '*' + s : s;
+ }
+}
diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/HashBag.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/HashBag.java
new file mode 100644
index 0000000000..d394bbb286
--- /dev/null
+++ b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/HashBag.java
@@ -0,0 +1,877 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2010 Oracle. 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:
+ * Oracle - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.jpt.common.utility.internal;
+
+import java.io.Serializable;
+import java.util.AbstractCollection;
+import java.util.Collection;
+import java.util.ConcurrentModificationException;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+/**
+ * This class implements the {@link Bag} interface, backed by a
+ * hash table. It makes no guarantees as to the iteration order of
+ * the bag's elements; in particular, it does not guarantee the order
+ * will remain constant over time. This class permits the <code>null</code>
+ * element.
+ * <p>
+ * This class offers constant time performance for the basic operations
+ * (<code>add</code>, <code>remove</code>, <code>contains</code> and
+ * <code>size</code>), assuming the hash function disperses the elements
+ * properly among the buckets. Iterating over this bag requires time
+ * proportional to the sum of the bag's size (the number of elements) plus the
+ * "capacity" of the backing hash table (the number of buckets). Thus, it is
+ * important not to set the initial capacity too high (or the load factor too
+ * low) if iteration performance is important.
+ * <p>
+ * <strong>Note that this implementation is not synchronized.</strong> If multiple
+ * threads access a bag concurrently, and at least one of the threads modifies
+ * the bag, it <em>must</em> be synchronized externally. This is typically
+ * accomplished by synchronizing on some object that naturally encapsulates
+ * the bag. If no such object exists, the bag should be "wrapped" using the
+ * <code>Collections.synchronizedCollection</code> method. This is
+ * best done at creation time, to prevent accidental unsynchronized access
+ * to the bag:
+ * <pre>
+ * Collection c = Collections.synchronizedCollection(new HashBag(...));
+ * </pre>
+ * <p>
+ * The iterators returned by this class's <code>iterator</code> method are
+ * <em>fail-fast</em>: if the bag is modified at any time after the iterator is
+ * created, in any way except through the iterator's own <code>remove</code>
+ * method, the iterator throws a {@link ConcurrentModificationException}.
+ * Thus, in the face of concurrent modification, the iterator fails quickly
+ * and cleanly, rather than risking arbitrary, non-deterministic behavior at
+ * an undetermined time in the future.
+ * <p>
+ * Note that the fail-fast behavior of an iterator cannot be guaranteed
+ * as it is, generally speaking, impossible to make any hard guarantees in the
+ * presence of unsynchronized concurrent modification. Fail-fast iterators
+ * throw <code>ConcurrentModificationException</code> on a best-effort basis.
+ * Therefore, it would be wrong to write a program that depended on this
+ * exception for its correctness: <em>the fail-fast behavior of iterators
+ * should be used only to detect bugs.</em>
+ *
+ * @param <E> the type of elements maintained by the bag
+ *
+ * @see Collection
+ * @see Bag
+ * @see SynchronizedBag
+ * @see Collections#synchronizedCollection(Collection)
+ * @see IdentityHashBag
+ */
+public class HashBag<E>
+ extends AbstractCollection<E>
+ implements Bag<E>, Cloneable, Serializable
+{
+ /** The hash table. Resized as necessary. Length MUST Always be a power of two. */
+ transient Entry<E>[] table;
+
+ /** The total number of entries in the bag. */
+ transient int size = 0;
+
+ /** The number of UNIQUE entries in the bag. */
+ transient int uniqueCount = 0;
+
+ /**
+ * The hash table is rehashed when its size exceeds this threshold. (The
+ * value of this field is <code>(int) (capacity * loadFactor)</code>.)
+ *
+ * @serial
+ */
+ private int threshold;
+
+ /**
+ * The load factor for the hash table.
+ *
+ * @serial
+ */
+ private final float loadFactor;
+
+ /**
+ * The number of times this bag has been structurally modified.
+ * Structural modifications are those that change the number of entries in
+ * the bag or otherwise modify its internal structure (e.g. rehash).
+ * This field is used to make iterators on this bag fail-fast.
+ *
+ * @see java.util.ConcurrentModificationException
+ */
+ transient int modCount = 0;
+
+ /**
+ * The default initial capacity - MUST be a power of two.
+ */
+ private static final int DEFAULT_INITIAL_CAPACITY = 16;
+
+ /**
+ * The maximum capacity, used if a higher value is implicitly specified
+ * by either of the constructors with arguments.
+ * MUST be a power of two <= (1 << 30).
+ */
+ private static final int MAXIMUM_CAPACITY = 1 << 30;
+
+ /**
+ * The load factor used when none specified in constructor.
+ */
+ private static final float DEFAULT_LOAD_FACTOR = 0.75f;
+
+ /**
+ * Construct a new, empty bag with the
+ * default capacity, which is 16, and load factor, which is 0.75.
+ */
+ public HashBag() {
+ this(DEFAULT_INITIAL_CAPACITY);
+ }
+
+ /**
+ * Construct a new, empty bag with the specified initial capacity
+ * and the default load factor, which is 0.75.
+ *
+ * @param initialCapacity the initial capacity
+ * @throws IllegalArgumentException if the initial capacity is less
+ * than zero
+ */
+ public HashBag(int initialCapacity) {
+ this(initialCapacity, DEFAULT_LOAD_FACTOR, false); // false = do not validate parms
+ }
+
+ /**
+ * Construct a new, empty bag with
+ * the specified initial capacity and load factor.
+ *
+ * @param initialCapacity the initial capacity
+ * @param loadFactor the load factor
+ * @throws IllegalArgumentException if the initial capacity is less
+ * than zero or if the load factor is non-positive
+ */
+ public HashBag(int initialCapacity, float loadFactor) {
+ this(initialCapacity, loadFactor, true); // true = validate parms
+ }
+
+ private HashBag(int initialCapacity, float loadFactor, boolean validateParms) {
+ super();
+ int capacity = initialCapacity;
+ if (validateParms) {
+ if (capacity < 0) {
+ throw new IllegalArgumentException("Illegal Initial Capacity: " + capacity); //$NON-NLS-1$
+ }
+ if (capacity > MAXIMUM_CAPACITY) {
+ capacity = MAXIMUM_CAPACITY;
+ }
+ if (loadFactor <= 0 || Float.isNaN(loadFactor)) {
+ throw new IllegalArgumentException("Illegal Load factor: " + loadFactor); //$NON-NLS-1$
+ }
+
+ // find a power of 2 >= 'initialCapacity'
+ capacity = 1;
+ while (capacity < initialCapacity) {
+ capacity <<= 1;
+ }
+ }
+
+ this.loadFactor = loadFactor;
+ this.table = this.buildTable(capacity);
+ this.threshold = (int) (capacity * loadFactor);
+ }
+
+ /**
+ * Construct a new bag containing the elements in the specified
+ * collection. The bag's load factor will be the default, which is 0.75,
+ * and its initial capacity will be sufficient to hold all the elements in
+ * the specified collection.
+ *
+ * @param c the collection whose elements are to be placed into this bag.
+ */
+ public HashBag(Collection<? extends E> c) {
+ this(Math.max((int) (c.size() / DEFAULT_LOAD_FACTOR) + 1, DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);
+ this.addAll_(c);
+ }
+
+ /**
+ * Return a hash for the specified object.
+ */
+ private int hash(Object o) {
+ return (o == null) ? 0 : this.tweakHash(o.hashCode());
+ }
+
+ /**
+ * Tweak the specified hash, to defend against poor implementations
+ * of {@link Object#hashCode()}.
+ */
+ private int tweakHash(int h) {
+ h ^= (h >>> 20) ^ (h >>> 12);
+ return h ^ (h >>> 7) ^ (h >>> 4);
+ }
+
+ /**
+ * Return the index for the specified hash.
+ */
+ private int index(int hash) {
+ return this.index(hash, this.table.length);
+ }
+
+ /**
+ * Return the index for the specified hash
+ * within a table with the specified length.
+ */
+ int index(int hash, int length) {
+ return hash & (length - 1);
+ }
+
+ /**
+ * Internal {@link #addAll(Collection)} for construction and cloning.
+ * (No check for re-hash; no change to mod count; no return value.)
+ */
+ private void addAll_(Iterable<? extends E> c) {
+ for (E e : c) {
+ this.add_(e);
+ }
+ }
+
+ /**
+ * Internal {@link #add(Object)} for construction and cloning.
+ * (No check for re-hash; no change to mod count; no return value.)
+ */
+ private void add_(E o) {
+ this.add_(o, 1);
+ }
+
+ /**
+ * Internal {@link #add(Object, int)} for construction, cloning, and serialization.
+ * (No check for re-hash; no change to mod count; no return value.)
+ */
+ private void add_(E o, int cnt) {
+ int hash = this.hash(o);
+ int index = this.index(hash);
+ for (Entry<E> e = this.table[index]; e != null; e = e.next) {
+ Object eo;
+ if ((e.hash == hash) && (((eo = e.object) == o) || ((o != null) && o.equals(eo)))) {
+ e.count += cnt;
+ this.size += cnt;
+ return;
+ }
+ }
+
+ // create the new entry and put it in the table
+ Entry<E> e = this.buildEntry(hash, o, cnt, this.table[index]);
+ this.table[index] = e;
+ this.size += cnt;
+ this.uniqueCount++;
+ }
+
+ /**
+ * This implementation simply returns the maintained size.
+ */
+ @Override
+ public int size() {
+ return this.size;
+ }
+
+ /**
+ * This implementation simply compares the maintained size to zero.
+ */
+ @Override
+ public boolean isEmpty() {
+ return this.size == 0;
+ }
+
+ /**
+ * Search for the object's entry in the hash table by calculating
+ * the object's hash code and examining the entries in the corresponding hash
+ * table bucket.
+ */
+ private Entry<E> getEntry(Object o) {
+ int hash = this.hash(o);
+ for (Entry<E> e = this.table[this.index(hash)]; e != null; e = e.next) {
+ Object eo;
+ if ((e.hash == hash) && (((eo = e.object) == o) || ((o != null) && o.equals(eo)))) {
+ return e;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public boolean contains(Object o) {
+ return this.getEntry(o) != null;
+ }
+
+ public int count(Object o) {
+ Entry<E> e = this.getEntry(o);
+ return (e == null) ? 0 : e.count;
+ }
+
+ /**
+ * Rehash the contents of the bag into a new hash table
+ * with a larger capacity. This method is called when the
+ * number of different elements in the bag exceeds its
+ * capacity and load factor.
+ */
+ private void rehash() {
+ Entry<E>[] oldTable = this.table;
+ int oldCapacity = oldTable.length;
+
+ if (oldCapacity == MAXIMUM_CAPACITY) {
+ this.threshold = Integer.MAX_VALUE;
+ return;
+ }
+
+ int newCapacity = 2 * oldCapacity;
+ Entry<E>[] newTable = this.buildTable(newCapacity);
+
+ for (int i = oldCapacity; i-- > 0; ) {
+ for (Entry<E> old = oldTable[i]; old != null; ) {
+ Entry<E> e = old;
+ old = old.next;
+
+ int index = this.index(e.hash, newCapacity);
+ e.next = newTable[index];
+ newTable[index] = e;
+ }
+ }
+
+ this.table = newTable;
+ this.threshold = (int) (newCapacity * this.loadFactor);
+ }
+
+ // minimize scope of suppressed warnings
+ @SuppressWarnings("unchecked")
+ private Entry<E>[] buildTable(int capacity) {
+ return new Entry[capacity];
+ }
+
+ /**
+ * This implementation searches for the object in the hash table by calculating
+ * the object's hash code and examining the entries in the corresponding hash
+ * table bucket.
+ */
+ @Override
+ public boolean add(E o) {
+ return this.add(o, 1);
+ }
+
+ /**
+ * This implementation searches for the object in the hash table by calculating
+ * the object's hash code and examining the entries in the corresponding hash
+ * table bucket.
+ */
+ public boolean add(E o, int cnt) {
+ if (cnt <= 0) {
+ return false;
+ }
+ this.modCount++;
+ int hash = this.hash(o);
+ int index = this.index(hash);
+
+ // if the object is already in the bag, simply bump its count
+ for (Entry<E> e = this.table[index]; e != null; e = e.next) {
+ Object eo;
+ if ((e.hash == hash) && (((eo = e.object) == o) || ((o != null) && o.equals(eo)))) {
+ e.count += cnt;
+ this.size += cnt;
+ return true;
+ }
+ }
+
+ // rehash the table if we are going to exceed the threshold
+ if (this.uniqueCount >= this.threshold) {
+ this.rehash();
+ index = this.index(hash); // need to re-calculate the index
+ }
+
+ // create the new entry and put it in the table
+ Entry<E> e = this.buildEntry(hash, o, cnt, this.table[index]);
+ this.table[index] = e;
+ this.size += cnt;
+ this.uniqueCount++;
+ return true;
+ }
+
+ // minimize scope of suppressed warnings
+ @SuppressWarnings({ "rawtypes", "unchecked" } )
+ private Entry<E> buildEntry(int hash, Object o, int cnt, Entry next) {
+ return new Entry<E>(hash, (E) o, cnt, (Entry<E>) next);
+ }
+
+ /**
+ * This implementation searches for the object in the hash table by calculating
+ * the object's hash code and examining the entries in the corresponding hash
+ * table bucket.
+ */
+ @Override
+ public boolean remove(Object o) {
+ return this.remove(o, 1);
+ }
+
+ /**
+ * This implementation searches for the object in the hash table by calculating
+ * the object's hash code and examining the entries in the corresponding hash
+ * table bucket.
+ */
+ public boolean remove(Object o, int cnt) {
+ if (cnt <= 0) {
+ return false;
+ }
+ int hash = this.hash(o);
+ int index = this.index(hash);
+
+ for (Entry<E> e = this.table[index], prev = null; e != null; prev = e, e = e.next) {
+ Object eo;
+ if ((e.hash == hash) && (((eo = e.object) == o) || ((o != null) && o.equals(eo)))) {
+ this.modCount++;
+ cnt = (cnt < e.count) ? cnt : e.count;
+ e.count -= cnt;
+ // if we are removing the last element(s), remove the entry from the table
+ if (e.count == 0) {
+ if (prev == null) {
+ this.table[index] = e.next;
+ } else {
+ prev.next = e.next;
+ }
+ this.uniqueCount--;
+ }
+ this.size -= cnt;
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * This implementation simply clears out all of the hash table buckets.
+ */
+ @Override
+ public void clear() {
+ Entry<E>[] tab = this.table;
+ this.modCount++;
+ for (int i = tab.length; i-- > 0; ) {
+ tab[i] = null;
+ }
+ this.size = 0;
+ this.uniqueCount = 0;
+ }
+
+ /**
+ * Returns a shallow copy of this bag: the elements
+ * themselves are not cloned.
+ *
+ * @return a shallow copy of this bag.
+ */
+ @Override
+ public HashBag<E> clone() {
+ try {
+ @SuppressWarnings("unchecked")
+ HashBag<E> clone = (HashBag<E>) super.clone();
+ clone.table = this.buildTable(this.table.length);
+ clone.size = 0;
+ clone.uniqueCount = 0;
+ clone.modCount = 0;
+ clone.addAll_(this);
+ return clone;
+ } catch (CloneNotSupportedException e) {
+ throw new InternalError();
+ }
+ }
+
+
+ /**
+ * Hash table collision list entry.
+ */
+ private static class Entry<E> implements Bag.Entry<E> {
+ final int hash;
+ final E object;
+ int count;
+ Entry<E> next;
+
+ Entry(int hash, E object, int count, Entry<E> next) {
+ this.hash = hash;
+ this.object = object;
+ this.count = count;
+ this.next = next;
+ }
+
+ // ***** Bag.Entry implementation
+ public E getElement() {
+ return this.object;
+ }
+
+ public int getCount() {
+ return this.count;
+ }
+
+ public int setCount(int count) {
+ if (count <= 0) {
+ throw new IllegalArgumentException("count must be greater than zero: " + count); //$NON-NLS-1$
+ }
+ int old = this.count;
+ this.count = count;
+ return old;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if ( ! (o instanceof Bag.Entry<?>)) {
+ return false;
+ }
+ @SuppressWarnings("rawtypes")
+ Bag.Entry e = (Bag.Entry) o;
+ return (this.count == e.getCount()) &&
+ Tools.valuesAreEqual(this.object, e.getElement());
+ }
+
+ @Override
+ public int hashCode() {
+ E o = this.object;
+ return (o == null) ? 0 : (this.count * o.hashCode());
+ }
+
+ @Override
+ public String toString() {
+ return this.object + "=>" + this.count; //$NON-NLS-1$
+ }
+ }
+
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public Iterator<E> iterator() {
+ return (this.size == 0) ? EMPTY_ITERATOR : new HashIterator();
+ }
+
+ @SuppressWarnings("unchecked")
+ public Iterator<E> uniqueIterator() {
+ return (this.size == 0) ? EMPTY_ITERATOR : new UniqueIterator();
+ }
+
+ public int uniqueCount() {
+ return this.uniqueCount;
+ }
+
+ @SuppressWarnings("unchecked")
+ public Iterator<Bag.Entry<E>> entries() {
+ return (this.size == 0) ? EMPTY_ITERATOR : new EntryIterator();
+ }
+
+
+ /**
+ * Empty iterator that does just about nothing.
+ */
+ @SuppressWarnings("rawtypes")
+ private static final Iterator EMPTY_ITERATOR = new EmptyIterator();
+
+ @SuppressWarnings("rawtypes")
+ private static class EmptyIterator implements Iterator {
+
+ EmptyIterator() {
+ super();
+ }
+
+ public boolean hasNext() {
+ return false;
+ }
+
+ public Object next() {
+ throw new NoSuchElementException();
+ }
+
+ public void remove() {
+ throw new IllegalStateException();
+ }
+ }
+
+
+ private class HashIterator implements Iterator<E> {
+ private int index = HashBag.this.table.length; // start at the end of the table
+ private Entry<E> nextEntry = null;
+ private int nextEntryCount = 0;
+ private Entry<E> lastReturnedEntry = null;
+
+ /**
+ * The modCount value that the iterator believes that the backing
+ * bag should have. If this expectation is violated, the iterator
+ * has detected a concurrent modification.
+ */
+ private int expectedModCount = HashBag.this.modCount;
+
+ HashIterator() {
+ super();
+ }
+
+ public boolean hasNext() {
+ Entry<E> e = this.nextEntry;
+ int i = this.index;
+ Entry<E>[] tab = HashBag.this.table;
+ // Use locals for faster loop iteration
+ while ((e == null) && (i > 0)) {
+ e = tab[--i]; // move backwards through the table
+ }
+ this.nextEntry = e;
+ this.index = i;
+ return e != null;
+ }
+
+ public E next() {
+ if (HashBag.this.modCount != this.expectedModCount) {
+ throw new ConcurrentModificationException();
+ }
+ Entry<E> et = this.nextEntry;
+ int i = this.index;
+ Entry<E>[] tab = HashBag.this.table;
+ // Use locals for faster loop iteration
+ while ((et == null) && (i > 0)) {
+ et = tab[--i]; // move backwards through the table
+ }
+ this.nextEntry = et;
+ this.index = i;
+ if (et == null) {
+ throw new NoSuchElementException();
+ }
+ Entry<E> e = this.lastReturnedEntry = this.nextEntry;
+ this.nextEntryCount++;
+ if (this.nextEntryCount == e.count) {
+ this.nextEntry = e.next;
+ this.nextEntryCount = 0;
+ }
+ return e.object;
+ }
+
+ public void remove() {
+ if (this.lastReturnedEntry == null) {
+ throw new IllegalStateException();
+ }
+ if (HashBag.this.modCount != this.expectedModCount) {
+ throw new ConcurrentModificationException();
+ }
+ int slot = HashBag.this.index(this.lastReturnedEntry.hash, HashBag.this.table.length);
+ for (Entry<E> e = HashBag.this.table[slot], prev = null; e != null; prev = e, e = e.next) {
+ if (e == this.lastReturnedEntry) {
+ HashBag.this.modCount++;
+ this.expectedModCount++;
+ e.count--;
+ if (e.count == 0) {
+ // if we are removing the last one, remove the entry from the table
+ if (prev == null) {
+ HashBag.this.table[slot] = e.next;
+ } else {
+ prev.next = e.next;
+ }
+ HashBag.this.uniqueCount--;
+ } else {
+ // slide back the count to account for the just-removed element
+ this.nextEntryCount--;
+ }
+ HashBag.this.size--;
+ this.lastReturnedEntry = null; // it cannot be removed again
+ return;
+ }
+ }
+ throw new ConcurrentModificationException();
+ }
+
+ }
+
+
+ private class EntryIterator implements Iterator<Entry<E>> {
+ private int index = HashBag.this.table.length; // start at the end of the table
+ private Entry<E> nextEntry = null;
+ private Entry<E> lastReturnedEntry = null;
+
+ /**
+ * The modCount value that the iterator believes that the backing
+ * bag should have. If this expectation is violated, the iterator
+ * has detected a concurrent modification.
+ */
+ private int expectedModCount = HashBag.this.modCount;
+
+ EntryIterator() {
+ super();
+ }
+
+ public boolean hasNext() {
+ Entry<E> e = this.nextEntry;
+ int i = this.index;
+ Entry<E>[] tab = HashBag.this.table;
+ // Use locals for faster loop iteration
+ while ((e == null) && (i > 0)) {
+ e = tab[--i]; // move backwards through the table
+ }
+ this.nextEntry = e;
+ this.index = i;
+ return e != null;
+ }
+
+ public Entry<E> next() {
+ if (HashBag.this.modCount != this.expectedModCount) {
+ throw new ConcurrentModificationException();
+ }
+ Entry<E> et = this.nextEntry;
+ int i = this.index;
+ Entry<E>[] tab = HashBag.this.table;
+ // Use locals for faster loop iteration
+ while ((et == null) && (i > 0)) {
+ et = tab[--i]; // move backwards through the table
+ }
+ this.nextEntry = et;
+ this.index = i;
+ if (et == null) {
+ throw new NoSuchElementException();
+ }
+ Entry<E> e = this.lastReturnedEntry = this.nextEntry;
+ this.nextEntry = e.next;
+ return e;
+ }
+
+ public void remove() {
+ if (this.lastReturnedEntry == null) {
+ throw new IllegalStateException();
+ }
+ if (HashBag.this.modCount != this.expectedModCount) {
+ throw new ConcurrentModificationException();
+ }
+ int slot = HashBag.this.index(this.lastReturnedEntry.hash, HashBag.this.table.length);
+ for (Entry<E> e = HashBag.this.table[slot], prev = null; e != null; prev = e, e = e.next) {
+ if (e == this.lastReturnedEntry) {
+ HashBag.this.modCount++;
+ this.expectedModCount++;
+ // remove the entry from the table
+ if (prev == null) {
+ HashBag.this.table[slot] = e.next;
+ } else {
+ prev.next = e.next;
+ }
+ HashBag.this.uniqueCount--;
+ HashBag.this.size -= this.lastReturnedEntry.count;
+ this.lastReturnedEntry = null; // it cannot be removed again
+ return;
+ }
+ }
+ throw new ConcurrentModificationException();
+ }
+
+ }
+
+
+ private class UniqueIterator implements Iterator<E> {
+ private EntryIterator entryIterator = new EntryIterator();
+
+ UniqueIterator() {
+ super();
+ }
+
+ public boolean hasNext() {
+ return this.entryIterator.hasNext();
+ }
+
+ public E next() {
+ return this.entryIterator.next().object;
+ }
+
+ public void remove() {
+ this.entryIterator.remove();
+ }
+
+ }
+
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ }
+ if ( ! (o instanceof Bag<?>)) {
+ return false;
+ }
+ @SuppressWarnings("unchecked")
+ Bag<E> b = (Bag<E>) o;
+ if (b.size() != this.size()) {
+ return false;
+ }
+ if (b.uniqueCount() != this.uniqueCount()) {
+ return false;
+ }
+ for (Iterator<Bag.Entry<E>> stream = b.entries(); stream.hasNext(); ) {
+ Bag.Entry<E> entry = stream.next();
+ if (entry.getCount() != this.count(entry.getElement())) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int h = 0;
+ for (E o : this) {
+ if (o != null) {
+ h += o.hashCode();
+ }
+ }
+ return h;
+ }
+
+ /**
+ * Save the state of this bag to a stream (i.e. serialize it).
+ *
+ * @serialData Emit the capacity of the bag (int),
+ * followed by the number of unique elements in the bag (int),
+ * followed by all of the bag's elements (each an Object) and
+ * their counts (each an int), in no particular order.
+ */
+ private void writeObject(java.io.ObjectOutputStream s)
+ throws java.io.IOException {
+ // write out the threshold, load factor, and any hidden stuff
+ s.defaultWriteObject();
+
+ // write out number of buckets
+ s.writeInt(this.table.length);
+
+ // write out number of unique elements
+ s.writeInt(this.uniqueCount);
+
+ // write out elements and counts (alternating)
+ if (this.uniqueCount > 0) {
+ for (Entry<E> entry : this.table) {
+ while (entry != null) {
+ s.writeObject(entry.object);
+ s.writeInt(entry.count);
+ entry = entry.next;
+ }
+ }
+ }
+ }
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Reconstitute the bag from a stream (i.e. deserialize it).
+ */
+ private void readObject(java.io.ObjectInputStream s)
+ throws java.io.IOException, ClassNotFoundException {
+ // read in the threshold, loadfactor, and any hidden stuff
+ s.defaultReadObject();
+
+ // read in number of buckets and allocate the bucket array
+ this.table = this.buildTable(s.readInt());
+
+ // read in number of unique elements
+ int unique = s.readInt();
+
+ // read the elements and counts, and put the elements in the bag
+ for (int i = 0; i < unique; i++) {
+ @SuppressWarnings("unchecked")
+ E element = (E) s.readObject();
+ int elementCount = s.readInt();
+ this.add_(element, elementCount);
+ }
+ }
+
+}
diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/IdentityHashBag.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/IdentityHashBag.java
new file mode 100644
index 0000000000..7c2fbc4a5c
--- /dev/null
+++ b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/IdentityHashBag.java
@@ -0,0 +1,924 @@
+/*******************************************************************************
+ * Copyright (c) 2007, 2010 Oracle. 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:
+ * Oracle - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.jpt.common.utility.internal;
+
+import java.io.Serializable;
+import java.util.AbstractCollection;
+import java.util.Collection;
+import java.util.ConcurrentModificationException;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+/**
+ * This class implements the {@link Bag} interface with a
+ * hash table, using object-identity in place of object-equality when
+ * comparing elements. In other words, in an <code>IdentityHashBag</code>,
+ * two objects <code>o1</code> and <code>o2</code> are considered
+ * equal if and only if <code>(o1 == o2)</code>. (In normal {@link Bag}
+ * implementations (like {@link HashBag}) two objects <code>o1</code>
+ * and <code>o2</code> are considered equal if and only if
+ * <code>(o1 == null ? o2 == null : o1.equals(o2))</code>.)
+ * <p>
+ * <strong>
+ * This class is <em>not</em> a general-purpose {@link Bag}
+ * implementation! While this class implements the {@link Bag} interface, it
+ * intentionally violates {@link Bag}'s general contract, which mandates the
+ * use of the <code>equals</code> method when comparing objects. This class is
+ * designed for use only in the rare cases wherein object-identity
+ * semantics are required.
+ * </strong>
+ * <p>
+ * This class makes no guarantees as to the iteration order of
+ * the bag's elements; in particular, it does not guarantee that the order
+ * will remain constant over time. This class permits the <code>null</code>
+ * element.
+ * <p>
+ * This class offers constant time performance for the basic operations
+ * (<code>add</code>, <code>remove</code>, <code>contains</code> and
+ * <code>size</code>), assuming the system identity hash function
+ * ({@link System#identityHashCode(Object)}) disperses elements properly
+ * among the buckets. Iterating over this bag requires time
+ * proportional to the sum of the bag's size (the number of elements) plus the
+ * "capacity" of the backing hash table (the number of buckets). Thus, it is
+ * important not to set the initial capacity too high (or the load factor too
+ * low) if iteration performance is important.
+ * <p>
+ * <strong>Note that this implementation is not synchronized.</strong> If multiple
+ * threads access a bag concurrently, and at least one of the threads modifies
+ * the bag, it <em>must</em> be synchronized externally. This is typically
+ * accomplished by synchronizing on some object that naturally encapsulates
+ * the bag. If no such object exists, the bag should be "wrapped" using the
+ * <code>Collections.synchronizedCollection</code> method. This is
+ * best done at creation time, to prevent accidental unsynchronized access
+ * to the bag:
+ * <pre>
+ * Collection c = Collections.synchronizedCollection(new IdentityHashBag(...));
+ * </pre>
+ * <p>
+ * The iterators returned by this class's <code>iterator</code> method are
+ * <em>fail-fast</em>: if the bag is modified at any time after the iterator is
+ * created, in any way except through the iterator's own <code>remove</code>
+ * method, the iterator throws a {@link ConcurrentModificationException}.
+ * Thus, in the face of concurrent modification, the iterator fails quickly
+ * and cleanly, rather than risking arbitrary, non-deterministic behavior at
+ * an undetermined time in the future.
+ * <p>
+ * Note that the fail-fast behavior of an iterator cannot be guaranteed
+ * as it is, generally speaking, impossible to make any hard guarantees in the
+ * presence of unsynchronized concurrent modification. Fail-fast iterators
+ * throw <code>ConcurrentModificationException</code> on a best-effort basis.
+ * Therefore, it would be wrong to write a program that depended on this
+ * exception for its correctness: <em>the fail-fast behavior of iterators
+ * should be used only to detect bugs.</em>
+ *
+ * @param <E> the type of elements maintained by the bag
+ *
+ * @see Collection
+ * @see Bag
+ * @see SynchronizedBag
+ * @see Collections#synchronizedCollection(Collection)
+ * @see HashBag
+ */
+public class IdentityHashBag<E>
+ extends AbstractCollection<E>
+ implements Bag<E>, Cloneable, Serializable
+{
+ /** The hash table. Resized as necessary. Length MUST Always be a power of two. */
+ transient Entry<E>[] table;
+
+ /** The total number of entries in the bag. */
+ transient int size = 0;
+
+ /** The number of UNIQUE entries in the bag. */
+ transient int uniqueCount = 0;
+
+ /**
+ * The hash table is rehashed when its size exceeds this threshold. (The
+ * value of this field is <code>(int) (capacity * loadFactor)</code>.)
+ *
+ * @serial
+ */
+ private int threshold;
+
+ /**
+ * The load factor for the hash table.
+ *
+ * @serial
+ */
+ private final float loadFactor;
+
+ /**
+ * The number of times this bag has been structurally modified.
+ * Structural modifications are those that change the number of entries in
+ * the bag or otherwise modify its internal structure (e.g. rehash).
+ * This field is used to make iterators on this bag fail-fast.
+ *
+ * @see java.util.ConcurrentModificationException
+ */
+ transient int modCount = 0;
+
+ /**
+ * The default initial capacity - MUST be a power of two.
+ */
+ private static final int DEFAULT_INITIAL_CAPACITY = 16;
+
+ /**
+ * The maximum capacity, used if a higher value is implicitly specified
+ * by either of the constructors with arguments.
+ * MUST be a power of two <= (1 << 30).
+ */
+ private static final int MAXIMUM_CAPACITY = 1 << 30;
+
+ /**
+ * The load factor used when none specified in constructor.
+ */
+ private static final float DEFAULT_LOAD_FACTOR = 0.75f;
+
+ /**
+ * Construct a new, empty bag with the
+ * default capacity, which is 16, and load factor, which is 0.75.
+ */
+ public IdentityHashBag() {
+ this(DEFAULT_INITIAL_CAPACITY);
+ }
+
+ /**
+ * Construct a new, empty bag with the specified initial capacity
+ * and the default load factor, which is 0.75.
+ *
+ * @param initialCapacity the initial capacity
+ * @throws IllegalArgumentException if the initial capacity is less
+ * than zero
+ */
+ public IdentityHashBag(int initialCapacity) {
+ this(initialCapacity, DEFAULT_LOAD_FACTOR, false); // false = do not validate parms
+ }
+
+ /**
+ * Construct a new, empty bag with
+ * the specified initial capacity and load factor.
+ *
+ * @param initialCapacity the initial capacity
+ * @param loadFactor the load factor
+ * @throws IllegalArgumentException if the initial capacity is less
+ * than zero or if the load factor is non-positive
+ */
+ public IdentityHashBag(int initialCapacity, float loadFactor) {
+ this(initialCapacity, loadFactor, true); // true = validate parms
+ }
+
+ private IdentityHashBag(int initialCapacity, float loadFactor, boolean validateParms) {
+ super();
+ int capacity = initialCapacity;
+ if (validateParms) {
+ if (capacity < 0) {
+ throw new IllegalArgumentException("Illegal Initial Capacity: " + capacity); //$NON-NLS-1$
+ }
+ if (capacity > MAXIMUM_CAPACITY) {
+ capacity = MAXIMUM_CAPACITY;
+ }
+ if (loadFactor <= 0 || Float.isNaN(loadFactor)) {
+ throw new IllegalArgumentException("Illegal Load factor: " + loadFactor); //$NON-NLS-1$
+ }
+
+ // find a power of 2 >= 'initialCapacity'
+ capacity = 1;
+ while (capacity < initialCapacity) {
+ capacity <<= 1;
+ }
+ }
+
+ this.loadFactor = loadFactor;
+ this.table = this.buildTable(capacity);
+ this.threshold = (int) (capacity * loadFactor);
+ }
+
+ /**
+ * Construct a new bag containing the elements in the specified
+ * collection. The bag's load factor will be the default, which is 0.75,
+ * and its initial capacity will be sufficient to hold all the elements in
+ * the specified collection.
+ *
+ * @param c the collection whose elements are to be placed into this bag.
+ */
+ public IdentityHashBag(Collection<? extends E> c) {
+ this(Math.max((int) (c.size() / DEFAULT_LOAD_FACTOR) + 1, DEFAULT_INITIAL_CAPACITY), DEFAULT_LOAD_FACTOR);
+ this.addAll_(c);
+ }
+
+ /**
+ * Return a index for the specified object.
+ */
+ private int index(Object o) {
+ return this.index(this.hash(o));
+ }
+
+ /**
+ * Return a hash for the specified object.
+ */
+ private int hash(Object o) {
+ return (o == null) ? 0 : this.tweakHash(System.identityHashCode(o));
+ }
+
+ /**
+ * Tweak the specified hash.
+ */
+ private int tweakHash(int h) {
+ return h;
+// h ^= (h >>> 20) ^ (h >>> 12);
+// return h ^ (h >>> 7) ^ (h >>> 4);
+ }
+
+ /**
+ * Return the index for the specified hash.
+ */
+ private int index(int hash) {
+ return this.index(hash, this.table.length);
+ }
+
+ /**
+ * Return the index for the specified hash
+ * within a table with the specified length.
+ */
+ int index(int hash, int length) {
+ return hash & (length - 1);
+ }
+
+ /**
+ * Internal {@link #addAll(Collection)} for construction and cloning.
+ * (No check for re-hash; no change to mod count; no return value.)
+ */
+ private void addAll_(Iterable<? extends E> c) {
+ for (E e : c) {
+ this.add_(e);
+ }
+ }
+
+ /**
+ * Internal {@link #add(Object)} for construction and cloning.
+ * (No check for re-hash; no change to mod count; no return value.)
+ */
+ private void add_(E o) {
+ this.add_(o, 1);
+ }
+
+ /**
+ * Internal {@link #add(Object, int)} for construction, cloning, and serialization.
+ * (No check for re-hash; no change to mod count; no return value.)
+ */
+ private void add_(E o, int cnt) {
+ int hash = this.hash(o);
+ int index = this.index(hash);
+ for (Entry<E> e = this.table[index]; e != null; e = e.next) {
+ if (e.object == o) {
+ e.count += cnt;
+ this.size += cnt;
+ return;
+ }
+ }
+
+ // create the new entry and put it in the table
+ Entry<E> e = this.buildEntry(hash, o, cnt, this.table[index]);
+ this.table[index] = e;
+ this.size += cnt;
+ this.uniqueCount++;
+ }
+
+ /**
+ * This implementation simply returns the maintained size.
+ */
+ @Override
+ public int size() {
+ return this.size;
+ }
+
+ /**
+ * This implementation simply compares the maintained size to zero.
+ */
+ @Override
+ public boolean isEmpty() {
+ return this.size == 0;
+ }
+
+ /**
+ * Search for the object's entry in the hash table by calculating
+ * the object's identity hash code and examining the entries in the corresponding hash
+ * table bucket.
+ */
+ private Entry<E> getEntry(Object o) {
+ for (Entry<E> e = this.table[this.index(o)]; e != null; e = e.next) {
+ if (e.object == o) {
+ return e;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public boolean contains(Object o) {
+ return this.getEntry(o) != null;
+ }
+
+ public int count(Object o) {
+ Entry<E> e = this.getEntry(o);
+ return (e == null) ? 0 : e.count;
+ }
+
+ /**
+ * Rehash the contents of the bag into a new hash table
+ * with a larger capacity. This method is called when the
+ * number of different elements in the bag exceeds its
+ * capacity and load factor.
+ */
+ private void rehash() {
+ Entry<E>[] oldTable = this.table;
+ int oldCapacity = oldTable.length;
+
+ if (oldCapacity == MAXIMUM_CAPACITY) {
+ this.threshold = Integer.MAX_VALUE;
+ return;
+ }
+
+ int newCapacity = 2 * oldCapacity;
+ Entry<E>[] newTable = this.buildTable(newCapacity);
+
+ for (int i = oldCapacity; i-- > 0; ) {
+ for (Entry<E> old = oldTable[i]; old != null; ) {
+ Entry<E> e = old;
+ old = old.next;
+
+ int index = this.index(e.hash, newCapacity);
+ e.next = newTable[index];
+ newTable[index] = e;
+ }
+ }
+
+ this.table = newTable;
+ this.threshold = (int) (newCapacity * this.loadFactor);
+ }
+
+ // minimize scope of suppressed warnings
+ @SuppressWarnings("unchecked")
+ private Entry<E>[] buildTable(int capacity) {
+ return new Entry[capacity];
+ }
+
+ /**
+ * This implementation searches for the object in the hash table by calculating
+ * the object's identity hash code and examining the entries in the corresponding hash
+ * table bucket.
+ */
+ @Override
+ public boolean add(E o) {
+ return this.add(o, 1);
+ }
+
+ /**
+ * This implementation searches for the object in the hash table by calculating
+ * the object's identity hash code and examining the entries in the corresponding hash
+ * table bucket.
+ */
+ public boolean add(E o, int cnt) {
+ if (cnt <= 0) {
+ return false;
+ }
+ this.modCount++;
+ int hash = this.hash(o);
+ int index = this.index(hash);
+
+ // if the object is already in the bag, simply bump its count
+ for (Entry<E> e = this.table[index]; e != null; e = e.next) {
+ if (e.object == o) {
+ e.count += cnt;
+ this.size += cnt;
+ return true;
+ }
+ }
+
+ // rehash the table if we are going to exceed the threshold
+ if (this.uniqueCount >= this.threshold) {
+ this.rehash();
+ index = this.index(hash); // need to re-calculate the index
+ }
+
+ // create the new entry and put it in the table
+ Entry<E> e = this.buildEntry(hash, o, cnt, this.table[index]);
+ this.table[index] = e;
+ this.size += cnt;
+ this.uniqueCount++;
+ return true;
+ }
+
+ // minimize scope of suppressed warnings
+ @SuppressWarnings({ "unchecked", "rawtypes" })
+ private Entry<E> buildEntry(int hash, Object o, int cnt, Entry next) {
+ return new Entry<E>(hash, (E) o, cnt, (Entry<E>) next);
+ }
+
+ /**
+ * This implementation searches for the object in the hash table by calculating
+ * the object's identity hash code and examining the entries in the corresponding hash
+ * table bucket.
+ */
+ @Override
+ public boolean remove(Object o) {
+ return this.remove(o, 1);
+ }
+
+ /**
+ * This implementation searches for the object in the hash table by calculating
+ * the object's identity hash code and examining the entries in the corresponding hash
+ * table bucket.
+ */
+ public boolean remove(Object o, int cnt) {
+ if (cnt <= 0) {
+ return false;
+ }
+ int index = this.index(o);
+
+ for (Entry<E> e = this.table[index], prev = null; e != null; prev = e, e = e.next) {
+ if (e.object == o) {
+ this.modCount++;
+ cnt = (cnt < e.count) ? cnt : e.count;
+ e.count -= cnt;
+ // if we are removing the last element(s), remove the entry from the table
+ if (e.count == 0) {
+ if (prev == null) {
+ this.table[index] = e.next;
+ } else {
+ prev.next = e.next;
+ }
+ this.uniqueCount--;
+ }
+ this.size -= cnt;
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * This implementation uses object-identity to determine whether the
+ * specified collection contains a particular element.
+ */
+ @Override
+ public boolean removeAll(Collection<?> c) {
+ return super.removeAll(new IdentityHashBag<Object>(c));
+ }
+
+ /**
+ * This implementation uses object-identity to determine whether the
+ * specified collection contains a particular element.
+ */
+ @Override
+ public boolean retainAll(Collection<?> c) {
+ return super.retainAll(new IdentityHashBag<Object>(c));
+ }
+
+ /**
+ * This implementation simply clears out all of the hash table buckets.
+ */
+ @Override
+ public void clear() {
+ Entry<E>[] tab = this.table;
+ this.modCount++;
+ for (int i = tab.length; i-- > 0; ) {
+ tab[i] = null;
+ }
+ this.size = 0;
+ this.uniqueCount = 0;
+ }
+
+ /**
+ * Returns a shallow copy of this bag: the elements
+ * themselves are not cloned.
+ *
+ * @return a shallow copy of this bag.
+ */
+ @Override
+ public IdentityHashBag<E> clone() {
+ try {
+ @SuppressWarnings("unchecked")
+ IdentityHashBag<E> clone = (IdentityHashBag<E>) super.clone();
+ clone.table = this.buildTable(this.table.length);
+ clone.size = 0;
+ clone.uniqueCount = 0;
+ clone.modCount = 0;
+ clone.addAll_(this);
+ return clone;
+ } catch (CloneNotSupportedException e) {
+ throw new InternalError();
+ }
+ }
+
+
+ /**
+ * Hash table collision list entry.
+ */
+ private static class Entry<E> implements Bag.Entry<E> {
+ final int hash; // cache the hash for re-hashes
+ final E object;
+ int count;
+ Entry<E> next;
+
+ Entry(int hash, E object, int count, Entry<E> next) {
+ this.hash = hash;
+ this.object = object;
+ this.count = count;
+ this.next = next;
+ }
+
+ // ***** Bag.Entry implementation
+ public E getElement() {
+ return this.object;
+ }
+
+ public int getCount() {
+ return this.count;
+ }
+
+ public int setCount(int count) {
+ if (count <= 0) {
+ throw new IllegalArgumentException("count must be greater than zero: " + count); //$NON-NLS-1$
+ }
+ int old = this.count;
+ this.count = count;
+ return old;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if ( ! (o instanceof Bag.Entry<?>)) {
+ return false;
+ }
+ @SuppressWarnings("rawtypes")
+ Bag.Entry e = (Bag.Entry) o;
+ return (this.object == e.getElement())
+ && (this.count == e.getCount());
+ }
+
+ @Override
+ public int hashCode() {
+ E o = this.object;
+ return (o == null) ? 0 : (this.count * o.hashCode());
+ }
+
+ @Override
+ public String toString() {
+ return this.object + "=>" + this.count; //$NON-NLS-1$
+ }
+ }
+
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public Iterator<E> iterator() {
+ return (this.size == 0) ? EMPTY_ITERATOR : new HashIterator();
+ }
+
+ @SuppressWarnings("unchecked")
+ public Iterator<E> uniqueIterator() {
+ return (this.size == 0) ? EMPTY_ITERATOR : new UniqueIterator();
+ }
+
+ public int uniqueCount() {
+ return this.uniqueCount;
+ }
+
+ @SuppressWarnings("unchecked")
+ public Iterator<Bag.Entry<E>> entries() {
+ return (this.size == 0) ? EMPTY_ITERATOR : new EntryIterator();
+ }
+
+
+ /**
+ * Empty iterator that does just about nothing.
+ */
+ @SuppressWarnings("rawtypes")
+ private static final Iterator EMPTY_ITERATOR = new EmptyIterator();
+
+ @SuppressWarnings("rawtypes")
+ private static class EmptyIterator implements Iterator {
+
+ EmptyIterator() {
+ super();
+ }
+
+ public boolean hasNext() {
+ return false;
+ }
+
+ public Object next() {
+ throw new NoSuchElementException();
+ }
+
+ public void remove() {
+ throw new IllegalStateException();
+ }
+ }
+
+
+ private class HashIterator implements Iterator<E> {
+ private int index = IdentityHashBag.this.table.length; // start at the end of the table
+ private Entry<E> nextEntry = null;
+ private int nextEntryCount = 0;
+ private Entry<E> lastReturnedEntry = null;
+
+ /**
+ * The modCount value that the iterator believes that the backing
+ * bag should have. If this expectation is violated, the iterator
+ * has detected a concurrent modification.
+ */
+ private int expectedModCount = IdentityHashBag.this.modCount;
+
+ HashIterator() {
+ super();
+ }
+
+ public boolean hasNext() {
+ Entry<E> e = this.nextEntry;
+ int i = this.index;
+ Entry<E>[] tab = IdentityHashBag.this.table;
+ // Use locals for faster loop iteration
+ while ((e == null) && (i > 0)) {
+ e = tab[--i]; // move backwards through the table
+ }
+ this.nextEntry = e;
+ this.index = i;
+ return e != null;
+ }
+
+ public E next() {
+ if (IdentityHashBag.this.modCount != this.expectedModCount) {
+ throw new ConcurrentModificationException();
+ }
+ Entry<E> et = this.nextEntry;
+ int i = this.index;
+ Entry<E>[] tab = IdentityHashBag.this.table;
+ // Use locals for faster loop iteration
+ while ((et == null) && (i > 0)) {
+ et = tab[--i]; // move backwards through the table
+ }
+ this.nextEntry = et;
+ this.index = i;
+ if (et == null) {
+ throw new NoSuchElementException();
+ }
+ Entry<E> e = this.lastReturnedEntry = this.nextEntry;
+ this.nextEntryCount++;
+ if (this.nextEntryCount == e.count) {
+ this.nextEntry = e.next;
+ this.nextEntryCount = 0;
+ }
+ return e.object;
+ }
+
+ public void remove() {
+ if (this.lastReturnedEntry == null) {
+ throw new IllegalStateException();
+ }
+ if (IdentityHashBag.this.modCount != this.expectedModCount) {
+ throw new ConcurrentModificationException();
+ }
+ int slot = IdentityHashBag.this.index(this.lastReturnedEntry.hash, IdentityHashBag.this.table.length);
+ for (Entry<E> e = IdentityHashBag.this.table[slot], prev = null; e != null; prev = e, e = e.next) {
+ if (e == this.lastReturnedEntry) {
+ IdentityHashBag.this.modCount++;
+ this.expectedModCount++;
+ e.count--;
+ if (e.count == 0) {
+ // if we are removing the last one, remove the entry from the table
+ if (prev == null) {
+ IdentityHashBag.this.table[slot] = e.next;
+ } else {
+ prev.next = e.next;
+ }
+ IdentityHashBag.this.uniqueCount--;
+ } else {
+ // slide back the count to account for the just-removed element
+ this.nextEntryCount--;
+ }
+ IdentityHashBag.this.size--;
+ this.lastReturnedEntry = null; // it cannot be removed again
+ return;
+ }
+ }
+ throw new ConcurrentModificationException();
+ }
+
+ }
+
+
+ private class EntryIterator implements Iterator<Entry<E>> {
+ private int index = IdentityHashBag.this.table.length; // start at the end of the table
+ private Entry<E> nextEntry = null;
+ private Entry<E> lastReturnedEntry = null;
+
+ /**
+ * The modCount value that the iterator believes that the backing
+ * bag should have. If this expectation is violated, the iterator
+ * has detected a concurrent modification.
+ */
+ private int expectedModCount = IdentityHashBag.this.modCount;
+
+ EntryIterator() {
+ super();
+ }
+
+ public boolean hasNext() {
+ Entry<E> e = this.nextEntry;
+ int i = this.index;
+ Entry<E>[] tab = IdentityHashBag.this.table;
+ // Use locals for faster loop iteration
+ while ((e == null) && (i > 0)) {
+ e = tab[--i]; // move backwards through the table
+ }
+ this.nextEntry = e;
+ this.index = i;
+ return e != null;
+ }
+
+ public Entry<E> next() {
+ if (IdentityHashBag.this.modCount != this.expectedModCount) {
+ throw new ConcurrentModificationException();
+ }
+ Entry<E> et = this.nextEntry;
+ int i = this.index;
+ Entry<E>[] tab = IdentityHashBag.this.table;
+ // Use locals for faster loop iteration
+ while ((et == null) && (i > 0)) {
+ et = tab[--i]; // move backwards through the table
+ }
+ this.nextEntry = et;
+ this.index = i;
+ if (et == null) {
+ throw new NoSuchElementException();
+ }
+ Entry<E> e = this.lastReturnedEntry = this.nextEntry;
+ this.nextEntry = e.next;
+ return e;
+ }
+
+ public void remove() {
+ if (this.lastReturnedEntry == null) {
+ throw new IllegalStateException();
+ }
+ if (IdentityHashBag.this.modCount != this.expectedModCount) {
+ throw new ConcurrentModificationException();
+ }
+ int slot = IdentityHashBag.this.index(this.lastReturnedEntry.hash, IdentityHashBag.this.table.length);
+ for (Entry<E> e = IdentityHashBag.this.table[slot], prev = null; e != null; prev = e, e = e.next) {
+ if (e == this.lastReturnedEntry) {
+ IdentityHashBag.this.modCount++;
+ this.expectedModCount++;
+ // remove the entry from the table
+ if (prev == null) {
+ IdentityHashBag.this.table[slot] = e.next;
+ } else {
+ prev.next = e.next;
+ }
+ IdentityHashBag.this.uniqueCount--;
+ IdentityHashBag.this.size -= this.lastReturnedEntry.count;
+ this.lastReturnedEntry = null; // it cannot be removed again
+ return;
+ }
+ }
+ throw new ConcurrentModificationException();
+ }
+
+ }
+
+
+ private class UniqueIterator implements Iterator<E> {
+ private EntryIterator entryIterator = new EntryIterator();
+
+ UniqueIterator() {
+ super();
+ }
+
+ public boolean hasNext() {
+ return this.entryIterator.hasNext();
+ }
+
+ public E next() {
+ return this.entryIterator.next().object;
+ }
+
+ public void remove() {
+ this.entryIterator.remove();
+ }
+
+ }
+
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == this) {
+ return true;
+ } else if (o instanceof IdentityHashBag<?>) {
+ @SuppressWarnings("unchecked")
+ IdentityHashBag<E> b = (IdentityHashBag<E>) o;
+ if (b.size() != this.size()) {
+ return false;
+ }
+ if (b.uniqueCount() != this.uniqueCount()) {
+ return false;
+ }
+ for (Iterator<Bag.Entry<E>> stream = b.entries(); stream.hasNext(); ) {
+ Bag.Entry<E> entry = stream.next();
+ if (entry.getCount() != this.count(entry.getElement())) {
+ return false;
+ }
+ }
+ return true;
+ } else {
+ return this.equals_(o);
+ }
+// } else if (o instanceof Bag<?>) {
+// // hmmm...
+// return new HashBag<Object>(this).equals(o);
+// } else {
+// return false;
+// }
+ }
+
+ private boolean equals_(Object o) {
+ // hmmm...
+ return (o instanceof Bag<?>) &&
+ new HashBag<Object>(this).equals(o);
+ }
+
+ @Override
+ public int hashCode() {
+ int h = 0;
+ for (E o : this) {
+ h += System.identityHashCode(o);
+ }
+ return h;
+ }
+
+ /**
+ * Save the state of this bag to a stream (i.e. serialize it).
+ *
+ * @serialData Emit the capacity of the bag (int),
+ * followed by the number of unique elements in the bag (int),
+ * followed by all of the bag's elements (each an Object) and
+ * their counts (each an int), in no particular order.
+ */
+ private void writeObject(java.io.ObjectOutputStream s)
+ throws java.io.IOException {
+ // write out the threshold, load factor, and any hidden stuff
+ s.defaultWriteObject();
+
+ // write out number of buckets
+ s.writeInt(this.table.length);
+
+ // write out number of unique elements
+ s.writeInt(this.uniqueCount);
+
+ // write out elements and counts (alternating)
+ if (this.uniqueCount > 0) {
+ for (Entry<E> entry : this.table) {
+ while (entry != null) {
+ s.writeObject(entry.object);
+ s.writeInt(entry.count);
+ entry = entry.next;
+ }
+ }
+ }
+ }
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Reconstitute the bag from a stream (i.e. deserialize it).
+ */
+ private void readObject(java.io.ObjectInputStream s)
+ throws java.io.IOException, ClassNotFoundException {
+ // read in the threshold, loadfactor, and any hidden stuff
+ s.defaultReadObject();
+
+ // read in number of buckets and allocate the bucket array
+ this.table = this.buildTable(s.readInt());
+
+ // read in number of unique elements
+ int unique = s.readInt();
+
+ // read the elements and counts, and put the elements in the bag
+ for (int i = 0; i < unique; i++) {
+ @SuppressWarnings("unchecked")
+ E element = (E) s.readObject();
+ int elementCount = s.readInt();
+ this.add_(element, elementCount);
+ }
+ }
+
+}
diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/IntReference.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/IntReference.java
new file mode 100644
index 0000000000..ad989b4fda
--- /dev/null
+++ b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/IntReference.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Oracle. 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:
+ * Oracle - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.jpt.common.utility.internal;
+
+/**
+ * Interface for a container for passing an integer that can be changed by
+ * the recipient.
+ */
+public interface IntReference
+ extends ReadOnlyIntReference
+{
+ /**
+ * Set the <code>int</code> value.
+ * Return the previous value.
+ */
+ int setValue(int value);
+
+ /**
+ * Set the <code>int</code> value to zero.
+ * Return the previous value.
+ */
+ int setZero();
+
+ /**
+ * Increment and return the <code>int</code> value.
+ */
+ int increment();
+
+ /**
+ * Decrement and return the <code>int</code> value.
+ */
+ int decrement();
+}
diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/JDBCTools.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/JDBCTools.java
new file mode 100644
index 0000000000..1663a8e493
--- /dev/null
+++ b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/JDBCTools.java
@@ -0,0 +1,349 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2010 Oracle. 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:
+ * Oracle - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.jpt.common.utility.internal;
+
+import java.sql.Types;
+import java.util.HashMap;
+import org.eclipse.jpt.common.utility.JavaType;
+
+/**
+ * Helper methods for dealing with the JDBC API.
+ */
+public final class JDBCTools {
+
+
+ /**
+ * Return the JDBC type corresponding to the specified class.
+ * @see java.sql.Types
+ */
+ public static JDBCType jdbcTypeForClassNamed(String className) {
+ JavaToJDBCTypeMapping mapping = javaToJDBCTypeMapping(className);
+ return (mapping == null) ? DEFAULT_JDBC_TYPE : mapping.getJDBCType();
+ }
+
+ /**
+ * Return the JDBC type corresponding to the specified class.
+ * @see java.sql.Types
+ */
+ public static JDBCType jdbcTypeFor(Class<?> javaClass) {
+ return jdbcTypeForClassNamed(javaClass.getName());
+ }
+
+ /**
+ * Return the JDBC type corresponding to the specified class.
+ * @see java.sql.Types
+ */
+ public static JDBCType jdbcTypeFor(JavaType javaType) {
+ return jdbcTypeForClassNamed(javaType.getJavaClassName());
+ }
+
+ /**
+ * Return the Java type corresponding to the specified JDBC type.
+ * @see java.sql.Types
+ */
+ public static JavaType javaTypeForJDBCTypeNamed(String jdbcTypeName) {
+ JDBCToJavaTypeMapping mapping = jdbcToJavaTypeMapping(jdbcTypeName);
+ return (mapping == null) ? DEFAULT_JAVA_TYPE : mapping.getJavaType();
+ }
+
+ /**
+ * Return the Java type corresponding to the specified JDBC type.
+ * @see java.sql.Types
+ */
+ public static JavaType javaTypeFor(JDBCType jdbcType) {
+ return javaTypeForJDBCTypeNamed(jdbcType.name());
+ }
+
+ /**
+ * Return the Java type corresponding to the specified JDBC type.
+ * @see java.sql.Types
+ */
+ public static JavaType javaTypeForJDBCTypeCode(int jdbcTypeCode) {
+ return javaTypeFor(JDBCType.type(jdbcTypeCode));
+ }
+
+
+ // ********** internal stuff **********
+
+
+ // ********** JDBC => Java **********
+
+ /**
+ * JDBC => Java type mappings, keyed by JDBC type name (e.g. "VARCHAR")
+ */
+ private static HashMap<String, JDBCToJavaTypeMapping> JDBC_TO_JAVA_TYPE_MAPPINGS; // pseudo 'final' - lazy-initialized
+ private static final JavaType DEFAULT_JAVA_TYPE = new SimpleJavaType(java.lang.Object.class); // TODO Object is the default?
+
+
+ private static JDBCToJavaTypeMapping jdbcToJavaTypeMapping(String jdbcTypeName) {
+ return jdbcToJavaTypeMappings().get(jdbcTypeName);
+ }
+
+ private static synchronized HashMap<String, JDBCToJavaTypeMapping> jdbcToJavaTypeMappings() {
+ if (JDBC_TO_JAVA_TYPE_MAPPINGS == null) {
+ JDBC_TO_JAVA_TYPE_MAPPINGS = buildJDBCToJavaTypeMappings();
+ }
+ return JDBC_TO_JAVA_TYPE_MAPPINGS;
+ }
+
+ private static HashMap<String, JDBCToJavaTypeMapping> buildJDBCToJavaTypeMappings() {
+ HashMap<String, JDBCToJavaTypeMapping> mappings = new HashMap<String, JDBCToJavaTypeMapping>();
+ addJDBCToJavaTypeMappingsTo(mappings);
+ return mappings;
+ }
+
+ /**
+ * hard code the default mappings from the JDBC types to the
+ * appropriate Java types
+ * @see java.sql.Types
+ * see "JDBC 3.0 Specification" Appendix B
+ */
+ private static void addJDBCToJavaTypeMappingsTo(HashMap<String, JDBCToJavaTypeMapping> mappings) {
+ addJDBCToJavaTypeMappingTo(Types.ARRAY, java.sql.Array.class, mappings);
+ addJDBCToJavaTypeMappingTo(Types.BIGINT, long.class, mappings);
+ addJDBCToJavaTypeMappingTo(Types.BINARY, byte[].class, mappings);
+ addJDBCToJavaTypeMappingTo(Types.BIT, boolean.class, mappings);
+ addJDBCToJavaTypeMappingTo(Types.BLOB, java.sql.Blob.class, mappings);
+ addJDBCToJavaTypeMappingTo(Types.BOOLEAN, boolean.class, mappings);
+ addJDBCToJavaTypeMappingTo(Types.CHAR, java.lang.String.class, mappings);
+ addJDBCToJavaTypeMappingTo(Types.CLOB, java.sql.Clob.class, mappings);
+ addJDBCToJavaTypeMappingTo(Types.DATALINK, java.net.URL.class, mappings);
+ addJDBCToJavaTypeMappingTo(Types.DATE, java.sql.Date.class, mappings);
+ addJDBCToJavaTypeMappingTo(Types.DECIMAL, java.math.BigDecimal.class, mappings);
+ addJDBCToJavaTypeMappingTo(Types.DISTINCT, java.lang.Object.class, mappings); // ???
+ addJDBCToJavaTypeMappingTo(Types.DOUBLE, double.class, mappings);
+ addJDBCToJavaTypeMappingTo(Types.FLOAT, double.class, mappings);
+ addJDBCToJavaTypeMappingTo(Types.INTEGER, int.class, mappings);
+ addJDBCToJavaTypeMappingTo(Types.JAVA_OBJECT, java.lang.Object.class, mappings); // ???
+ addJDBCToJavaTypeMappingTo(Types.LONGVARBINARY, byte[].class, mappings);
+ addJDBCToJavaTypeMappingTo(Types.LONGVARCHAR, java.lang.String.class, mappings);
+ // not sure why this is defined in java.sql.Types
+// addJDBCToJavaTypeMappingTo(Types.NULL, java.lang.Object.class, mappings);
+ addJDBCToJavaTypeMappingTo(Types.NUMERIC, java.math.BigDecimal.class, mappings);
+ addJDBCToJavaTypeMappingTo(Types.OTHER, java.lang.Object.class, mappings); // ???
+ addJDBCToJavaTypeMappingTo(Types.REAL, float.class, mappings);
+ addJDBCToJavaTypeMappingTo(Types.REF, java.sql.Ref.class, mappings);
+ addJDBCToJavaTypeMappingTo(Types.SMALLINT, short.class, mappings);
+ addJDBCToJavaTypeMappingTo(Types.STRUCT, java.sql.Struct.class, mappings);
+ addJDBCToJavaTypeMappingTo(Types.TIME, java.sql.Time.class, mappings);
+ addJDBCToJavaTypeMappingTo(Types.TIMESTAMP, java.sql.Timestamp.class, mappings);
+ addJDBCToJavaTypeMappingTo(Types.TINYINT, byte.class, mappings);
+ addJDBCToJavaTypeMappingTo(Types.VARBINARY, byte[].class, mappings);
+ addJDBCToJavaTypeMappingTo(Types.VARCHAR, java.lang.String.class, mappings);
+ }
+
+ private static void addJDBCToJavaTypeMappingTo(int jdbcTypeCode, Class<?> javaClass, HashMap<String, JDBCToJavaTypeMapping> mappings) {
+ // check for duplicates
+ JDBCType jdbcType = JDBCType.type(jdbcTypeCode);
+ Object prev = mappings.put(jdbcType.name(), buildJDBCToJavaTypeMapping(jdbcType, javaClass));
+ if (prev != null) {
+ throw new IllegalArgumentException("duplicate JDBC type: " + jdbcType.name()); //$NON-NLS-1$
+ }
+ }
+
+ private static JDBCToJavaTypeMapping buildJDBCToJavaTypeMapping(JDBCType jdbcType, Class<?> javaClass) {
+ return new JDBCToJavaTypeMapping(jdbcType, new SimpleJavaType(javaClass));
+ }
+
+
+ // ********** Java => JDBC **********
+
+ /**
+ * Java => JDBC type mappings, keyed by Java class name (e.g. "java.lang.Object")
+ */
+ private static HashMap<String, JavaToJDBCTypeMapping> JAVA_TO_JDBC_TYPE_MAPPINGS; // pseudo 'final' - lazy-initialized
+ private static final JDBCType DEFAULT_JDBC_TYPE = JDBCType.type(Types.VARCHAR); // TODO VARCHAR is the default?
+
+
+ private static JavaToJDBCTypeMapping javaToJDBCTypeMapping(String className) {
+ return javaToJDBCTypeMappings().get(className);
+ }
+
+ private static synchronized HashMap<String, JavaToJDBCTypeMapping> javaToJDBCTypeMappings() {
+ if (JAVA_TO_JDBC_TYPE_MAPPINGS == null) {
+ JAVA_TO_JDBC_TYPE_MAPPINGS = buildJavaToJDBCTypeMappings();
+ }
+ return JAVA_TO_JDBC_TYPE_MAPPINGS;
+ }
+
+ private static HashMap<String, JavaToJDBCTypeMapping> buildJavaToJDBCTypeMappings() {
+ HashMap<String, JavaToJDBCTypeMapping> mappings = new HashMap<String, JavaToJDBCTypeMapping>();
+ addJavaToJDBCTypeMappingsTo(mappings);
+ return mappings;
+ }
+
+ /**
+ * hard code the default mappings from the Java types to the
+ * appropriate JDBC types
+ * @see java.sql.Types
+ * see "JDBC 3.0 Specification" Appendix B
+ */
+ private static void addJavaToJDBCTypeMappingsTo(HashMap<String, JavaToJDBCTypeMapping> mappings) {
+ // primitives
+ addJavaToJDBCTypeMappingTo(boolean.class, Types.BIT, mappings);
+ addJavaToJDBCTypeMappingTo(byte.class, Types.TINYINT, mappings);
+ addJavaToJDBCTypeMappingTo(double.class, Types.DOUBLE, mappings);
+ addJavaToJDBCTypeMappingTo(float.class, Types.REAL, mappings);
+ addJavaToJDBCTypeMappingTo(int.class, Types.INTEGER, mappings);
+ addJavaToJDBCTypeMappingTo(long.class, Types.BIGINT, mappings);
+ addJavaToJDBCTypeMappingTo(short.class, Types.SMALLINT, mappings);
+
+ // reference classes
+ addJavaToJDBCTypeMappingTo(java.lang.Boolean.class, Types.BIT, mappings);
+ addJavaToJDBCTypeMappingTo(java.lang.Byte.class, Types.TINYINT, mappings);
+ addJavaToJDBCTypeMappingTo(java.lang.Double.class, Types.DOUBLE, mappings);
+ addJavaToJDBCTypeMappingTo(java.lang.Float.class, Types.REAL, mappings);
+ addJavaToJDBCTypeMappingTo(java.lang.Integer.class, Types.INTEGER, mappings);
+ addJavaToJDBCTypeMappingTo(java.lang.Long.class, Types.BIGINT, mappings);
+ addJavaToJDBCTypeMappingTo(java.lang.Short.class, Types.SMALLINT, mappings);
+ addJavaToJDBCTypeMappingTo(java.lang.String.class, Types.VARCHAR, mappings);
+ addJavaToJDBCTypeMappingTo(java.math.BigDecimal.class, Types.NUMERIC, mappings);
+ addJavaToJDBCTypeMappingTo(java.net.URL.class, Types.DATALINK, mappings);
+ addJavaToJDBCTypeMappingTo(java.sql.Array.class, Types.ARRAY, mappings);
+ addJavaToJDBCTypeMappingTo(java.sql.Blob.class, Types.BLOB, mappings);
+ addJavaToJDBCTypeMappingTo(java.sql.Clob.class, Types.CLOB, mappings);
+ addJavaToJDBCTypeMappingTo(java.sql.Date.class, Types.DATE, mappings);
+ addJavaToJDBCTypeMappingTo(java.sql.Ref.class, Types.REF, mappings);
+ addJavaToJDBCTypeMappingTo(java.sql.Struct.class, Types.STRUCT, mappings);
+ addJavaToJDBCTypeMappingTo(java.sql.Time.class, Types.TIME, mappings);
+ addJavaToJDBCTypeMappingTo(java.sql.Timestamp.class, Types.TIMESTAMP, mappings);
+
+ // arrays
+ addJavaToJDBCTypeMappingTo(byte[].class, Types.VARBINARY, mappings);
+ addJavaToJDBCTypeMappingTo(java.lang.Byte[].class, Types.VARBINARY, mappings);
+ }
+
+ private static void addJavaToJDBCTypeMappingTo(Class<?> javaClass, int jdbcTypeCode, HashMap<String, JavaToJDBCTypeMapping> mappings) {
+ // check for duplicates
+ Object prev = mappings.put(javaClass.getName(), buildJavaToJDBCTypeMapping(javaClass, jdbcTypeCode));
+ if (prev != null) {
+ throw new IllegalArgumentException("duplicate Java class: " + ((JavaToJDBCTypeMapping) prev).getJavaType().declaration()); //$NON-NLS-1$
+ }
+ }
+
+ private static JavaToJDBCTypeMapping buildJavaToJDBCTypeMapping(Class<?> javaClass, int jdbcTypeCode) {
+ return new JavaToJDBCTypeMapping(new SimpleJavaType(javaClass), JDBCType.type(jdbcTypeCode));
+ }
+
+
+ // ********** constructor **********
+
+ /**
+ * Suppress default constructor, ensuring non-instantiability.
+ */
+ private JDBCTools() {
+ super();
+ throw new UnsupportedOperationException();
+ }
+
+
+ // ********** member classes **********
+
+ /**
+ * JDBC => Java
+ */
+ static class JDBCToJavaTypeMapping {
+ private final JDBCType jdbcType;
+ private final JavaType javaType;
+
+ JDBCToJavaTypeMapping(JDBCType jdbcType, JavaType javaType) {
+ super();
+ this.jdbcType = jdbcType;
+ this.javaType = javaType;
+ }
+
+ public JDBCType getJDBCType() {
+ return this.jdbcType;
+ }
+
+ public JavaType getJavaType() {
+ return this.javaType;
+ }
+
+ public boolean maps(int jdbcTypeCode) {
+ return this.jdbcType.code() == jdbcTypeCode;
+ }
+
+ public boolean maps(String jdbcTypeName) {
+ return this.jdbcType.name().equals(jdbcTypeName);
+ }
+
+ public boolean maps(JDBCType type) {
+ return this.jdbcType == type;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ this.appendTo(sb);
+ return sb.toString();
+ }
+
+ public void appendTo(StringBuilder sb) {
+ this.jdbcType.appendTo(sb);
+ sb.append(" => "); //$NON-NLS-1$
+ this.javaType.appendDeclarationTo(sb);
+ }
+
+ }
+
+ /**
+ * Java => JDBC
+ */
+ static class JavaToJDBCTypeMapping {
+ private final JavaType javaType;
+ private final JDBCType jdbcType;
+
+ JavaToJDBCTypeMapping(JavaType javaType, JDBCType jdbcType) {
+ super();
+ this.javaType = javaType;
+ this.jdbcType = jdbcType;
+ }
+
+ public JavaType getJavaType() {
+ return this.javaType;
+ }
+
+ public JDBCType getJDBCType() {
+ return this.jdbcType;
+ }
+
+ public boolean maps(JavaType jt) {
+ return this.javaType.equals(jt);
+ }
+
+ public boolean maps(String elementTypeName, int arrayDepth) {
+ return this.javaType.equals(elementTypeName, arrayDepth);
+ }
+
+ public boolean maps(String javaClassName) {
+ return this.javaType.describes(javaClassName);
+ }
+
+ public boolean maps(Class<?> javaClass) {
+ return this.javaType.describes(javaClass);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ this.appendTo(sb);
+ return sb.toString();
+ }
+
+ public void appendTo(StringBuilder sb) {
+ this.javaType.appendDeclarationTo(sb);
+ sb.append(" => "); //$NON-NLS-1$
+ this.jdbcType.appendTo(sb);
+ }
+
+ }
+
+}
diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/JDBCType.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/JDBCType.java
new file mode 100644
index 0000000000..96fd283a7d
--- /dev/null
+++ b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/JDBCType.java
@@ -0,0 +1,162 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2010 Oracle. 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:
+ * Oracle - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.jpt.common.utility.internal;
+
+import java.io.Serializable;
+import java.lang.reflect.Field;
+import java.sql.Types;
+
+/**
+ * Associate the Java constant and the JDBC type name.
+ * These are derived from java.sql.Types.
+ *
+ * @see java.sql.Types
+ */
+public final class JDBCType
+ implements Cloneable, Serializable
+{
+
+ /**
+ * the constant name (e.g. VARCHAR)
+ */
+ private final String name;
+
+ /**
+ * the JDBC code used by JDBC drivers
+ */
+ private final int code;
+
+ private static final long serialVersionUID = 1L;
+
+
+ // ********** constructors **********
+
+ /**
+ * Construct a JDBC type with the specified name and type code.
+ * This is private because all the possible JDBC types are built and
+ * stored in the static array TYPES.
+ * @see #types()
+ */
+ private JDBCType(String name, int code) {
+ super();
+ this.name = name;
+ this.code = code;
+ }
+
+
+ // ********** accessors **********
+
+ /**
+ * Return the name of the type, as defined in java.sql.Types.
+ */
+ public String name() {
+ return this.name;
+ }
+
+
+ /**
+ * Return the type code, as defined in java.sql.Types.
+ */
+ public int code() {
+ return this.code;
+ }
+
+
+ // ********** printing and displaying **********
+
+ public void appendTo(StringBuilder sb) {
+ sb.append(this.name);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(this.getClass().getSimpleName());
+ sb.append('(');
+ this.appendTo(sb);
+ sb.append(')');
+ return sb.toString();
+ }
+
+ @Override
+ public JDBCType clone() {
+ try {
+ return (JDBCType) super.clone();
+ } catch (CloneNotSupportedException ex) {
+ throw new InternalError();
+ }
+ }
+
+
+ // ********** static stuff **********
+
+ /**
+ * all the JDBC type defined in java.sql.Types
+ */
+ private static JDBCType[] TYPES; // pseudo 'final' - lazy-initialized
+
+
+ public synchronized static JDBCType[] types() {
+ if (TYPES == null) {
+ TYPES = buildTypes();
+ }
+ return TYPES;
+ }
+
+ /**
+ * Return the JDBC type for the specified type code (e.g. Types.VARCHAR).
+ * @see java.sql.Types
+ */
+ public static JDBCType type(int code) {
+ JDBCType[] types = types();
+ for (int i = types.length; i-- > 0; ) {
+ if (types[i].code() == code) {
+ return types[i];
+ }
+ }
+ throw new IllegalArgumentException("invalid JDBC type code: " + code); //$NON-NLS-1$
+ }
+
+ /**
+ * Return the JDBC type for the specified type name (e.g. "VARCHAR").
+ * @see java.sql.Types
+ */
+ public static JDBCType type(String name) {
+ JDBCType[] types = types();
+ for (int i = types.length; i-- > 0; ) {
+ if (types[i].name().equals(name)) {
+ return types[i];
+ }
+ }
+ throw new IllegalArgumentException("invalid JDBC type name: " + name); //$NON-NLS-1$
+ }
+
+ /**
+ * build up the JDBC types via reflection
+ * @see java.sql.Types
+ */
+ private static JDBCType[] buildTypes() {
+ Field[] fields = Types.class.getDeclaredFields();
+ int len = fields.length;
+ JDBCType[] types = new JDBCType[len];
+ for (int i = len; i-- > 0; ) {
+ String name = fields[i].getName();
+ int code;
+ try {
+ code = ((Integer) fields[i].get(null)).intValue();
+ } catch (IllegalAccessException ex) {
+ throw new RuntimeException(ex); // shouldn't happen...
+ }
+ types[i] = new JDBCType(name, code);
+ }
+ return types;
+ }
+
+}
diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/KeyedSet.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/KeyedSet.java
new file mode 100644
index 0000000000..cf471f4bb8
--- /dev/null
+++ b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/KeyedSet.java
@@ -0,0 +1,129 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Oracle. 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:
+ * Oracle - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jpt.common.utility.internal;
+
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * This class maintains a {@link Set} of items, and a {@link Map} of keys to those items.
+ * An item may have multiple keys, but an item may have no keys and remain in the set. Once an
+ * item's last key is removed, the item is also removed.
+ */
+public class KeyedSet<K, V> {
+
+ private final Set<V> itemSet;
+ private final Set<V> unmodifiableItemSet;
+ private final Map<K,V> map;
+
+
+ public KeyedSet() {
+ this.itemSet = new HashSet<V>();
+ this.unmodifiableItemSet = Collections.unmodifiableSet(this.itemSet);
+ this.map = new HashMap<K,V>();
+ }
+
+ /**
+ * Return an unmodifiable representation of the set of items.
+ */
+ public Set<V> getItemSet() {
+ return this.unmodifiableItemSet;
+ }
+
+ /**
+ * Return the item stored under the given key.
+ */
+ public V getItem(K key) {
+ return this.map.get(key);
+ }
+
+ /**
+ * Return whether an item is stored under the given key.
+ */
+ public boolean containsKey(K key) {
+ return this.map.containsKey(key);
+ }
+
+ /**
+ * Return whether the item is stored under *any* key.
+ */
+ public boolean containsItem(V item) {
+ return this.itemSet.contains(item);
+ }
+
+ /**
+ * Add an item to be stored under the given key.
+ * The item must not already be stored.
+ */
+ public void addItem(K key, V item) {
+ addItem(item);
+ addKey(key, item);
+ }
+
+ private void addItem(V item) {
+ if (item == null) {
+ throw new IllegalArgumentException();
+ }
+ this.itemSet.add(item);
+ }
+
+ /**
+ * Add an additional key to an item already stored under an alternate key.
+ */
+ public void addKey(K key, V item) {
+ if (key == null || item == null) {
+ throw new IllegalArgumentException();
+ }
+ if (! this.itemSet.contains(item)) {
+ throw new IllegalArgumentException();
+ }
+ this.map.put(key, item);
+ }
+
+ /**
+ * Remove the given item and remove any key-to-item mapping it may have.
+ */
+ public boolean removeItem(V item) {
+ if (this.itemSet.remove(item)) {
+ for (Map.Entry<K,V> entry : CollectionTools.collection(this.map.entrySet())) {
+ if (entry.getValue() == item) {
+ map.remove(entry.getKey());
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Remove the key-to-item mapping for the given key.
+ * If it is the last key to the item, also remove the item.
+ */
+ public boolean removeKey(K key) {
+ final V item = this.map.get(key);
+ if (item != null) {
+ this.map.remove(key);
+ boolean otherKey = false;
+ for (Map.Entry<K,V> entry : CollectionTools.collection(this.map.entrySet())) {
+ if (otherKey | entry.getValue() == item) {
+ otherKey = true;
+ }
+ }
+ if (! otherKey) {
+ removeItem(item);
+ }
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/LazyReadOnlyObjectReference.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/LazyReadOnlyObjectReference.java
new file mode 100644
index 0000000000..84d01e34c3
--- /dev/null
+++ b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/LazyReadOnlyObjectReference.java
@@ -0,0 +1,107 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Oracle. 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:
+ * Oracle - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.jpt.common.utility.internal;
+
+import java.io.Serializable;
+import org.eclipse.jpt.common.utility.ReadOnlyObjectReference;
+
+/**
+ * Provide a thread-safe, reasonably performing container for holding an
+ * object that will be "lazy-initialized" upon its first reference. This is
+ * also useful for preventing direct references (accidental or otherwise) to
+ * lazy-initialized state.
+ * <p>
+ * There are some penalties:<ul>
+ * <li>The reference's use of generics will require casting (and the requisite
+ * VM testing) with every access
+ * <li>If the value calculated during lazy initialization is <code>null</code>,
+ * access will be <code>synchronized</code> <em>every</em> time.
+ * </ul>
+ * @see SimpleObjectReference
+ * @see SynchronizedObject
+ */
+public abstract class LazyReadOnlyObjectReference<V>
+ implements ReadOnlyObjectReference<V>, Cloneable, Serializable
+{
+ /** Backing value. */
+ private volatile V value = null;
+
+ private static final long serialVersionUID = 1L;
+
+
+ // ********** constructors **********
+
+ /**
+ * Create a lazy object reference.
+ */
+ public LazyReadOnlyObjectReference() {
+ super();
+ }
+
+
+ // ********** value **********
+
+ /**
+ * In JDK 5 and later, this "double-checked locking" idiom works as long
+ * as the instance variable is marked <code>volatile</code>.
+ */
+ public V getValue() {
+ V result = this.value;
+ if (result == null) {
+ synchronized (this) {
+ result = this.value;
+ if (result == null) {
+ this.value = result = this.buildValue();
+ }
+ }
+ }
+ return result;
+ }
+
+ protected abstract V buildValue();
+
+ public boolean valueEquals(Object object) {
+ return Tools.valuesAreEqual(this.getValue(), object);
+ }
+
+ public boolean valueNotEqual(Object object) {
+ return Tools.valuesAreDifferent(this.getValue(), object);
+ }
+
+ public boolean isNull() {
+ return this.getValue() == null;
+ }
+
+ public boolean isNotNull() {
+ return this.getValue() != null;
+ }
+
+
+ // ********** standard methods **********
+
+ @Override
+ public LazyReadOnlyObjectReference<V> clone() {
+ try {
+ @SuppressWarnings("unchecked")
+ LazyReadOnlyObjectReference<V> clone = (LazyReadOnlyObjectReference<V>) super.clone();
+ return clone;
+ } catch (CloneNotSupportedException ex) {
+ throw new InternalError();
+ }
+ }
+
+ /**
+ * This method will <em>not</em> trigger the "lazy-initialization".
+ */
+ @Override
+ public String toString() {
+ return '[' + String.valueOf(this.value) + ']';
+ }
+}
diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/ListenerList.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/ListenerList.java
new file mode 100644
index 0000000000..632b3fd5df
--- /dev/null
+++ b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/ListenerList.java
@@ -0,0 +1,171 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 Oracle. 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:
+ * Oracle - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.jpt.common.utility.internal;
+
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
+import java.lang.reflect.Array;
+import java.util.Arrays;
+import java.util.EventListener;
+
+import org.eclipse.jpt.common.utility.internal.iterables.ArrayIterable;
+
+/**
+ * Maintain a thread-safe list of listeners that does not allow adding
+ * duplicate listeners or removing non-listeners.
+ */
+public class ListenerList<L extends EventListener>
+ implements Serializable
+{
+ /**
+ * We can mark this volatile and not synchronize the read methods because
+ * we never change the <em>contents</em> of the array.
+ */
+ private transient volatile L[] listeners;
+
+ private static final long serialVersionUID = 1L;
+
+
+ /**
+ * Construct a listener list for listeners of the specified type.
+ */
+ public ListenerList(Class<L> listenerClass) {
+ super();
+ this.listeners = this.buildListenerArray(listenerClass, 0);
+ }
+
+ /**
+ * Construct a listener list for listeners of the specified type.
+ * Add the specified listener to the list.
+ */
+ public ListenerList(Class<L> listenerClass, L listener) {
+ super();
+ if (listener == null) {
+ throw new NullPointerException();
+ }
+ this.listeners = this.buildListenerArray(listenerClass, 1);
+ this.listeners[0] = listener;
+ }
+
+ @SuppressWarnings("unchecked")
+ private L[] buildListenerArray(Class<L> listenerClass, int length) {
+ return (L[]) Array.newInstance(listenerClass, length);
+ }
+
+ /**
+ * Return the listeners.
+ */
+ public Iterable<L> getListeners() {
+ return new ArrayIterable<L>(this.listeners);
+ }
+
+ /**
+ * Return the number of listeners.
+ */
+ public int size() {
+ return this.listeners.length;
+ }
+
+ /**
+ * Return whether the listener list has no listeners.
+ */
+ public boolean isEmpty() {
+ return this.listeners.length == 0;
+ }
+
+ /**
+ * Add the specified listener to the listener list.
+ * Duplicate listeners are not allowed.
+ */
+ public synchronized void add(L listener) {
+ if (listener == null) {
+ throw new NullPointerException();
+ }
+ if (ArrayTools.contains(this.listeners, listener)) {
+ throw new IllegalArgumentException("duplicate listener: " + listener); //$NON-NLS-1$
+ }
+ this.listeners = ArrayTools.add(this.listeners, listener);
+ }
+
+ /**
+ * Remove the specified listener from the listener list.
+ * Removing a listener that is not on the list is not allowed.
+ */
+ public synchronized void remove(L listener) {
+ if (listener == null) {
+ throw new NullPointerException();
+ }
+ int index = ArrayTools.indexOf(this.listeners, listener);
+ if (index == -1) {
+ throw new IllegalArgumentException("unregistered listener: " + listener); //$NON-NLS-1$
+ }
+ this.listeners = ArrayTools.removeElementAtIndex(this.listeners, index);
+ }
+
+ /**
+ * Clear the listener list.
+ */
+ public synchronized void clear() {
+ this.listeners = ArrayTools.clear(this.listeners);
+ }
+
+ /**
+ * Return the type of listeners held by the listener list.
+ */
+ @SuppressWarnings("unchecked")
+ public Class<L> getListenerType() {
+ return (Class<L>) this.listeners.getClass().getComponentType();
+ }
+
+ @Override
+ public String toString() {
+ return Arrays.toString(this.listeners);
+ }
+
+
+ // ********** serialization **********
+
+ /**
+ * Silently drop any non-serializable listeners.
+ */
+ private synchronized void writeObject(ObjectOutputStream s) throws IOException {
+ // write out any hidden stuff
+ s.defaultWriteObject();
+
+ @SuppressWarnings("unchecked")
+ Class<L> listenerClass = (Class<L>) this.listeners.getClass().getComponentType();
+ s.writeObject(listenerClass);
+
+ // only write out serializable listeners
+ for (L listener : this.listeners) {
+ if (listener instanceof Serializable) {
+ s.writeObject(listener);
+ }
+ }
+
+ s.writeObject(null);
+ }
+
+ @SuppressWarnings("unchecked")
+ private void readObject(ObjectInputStream s) throws ClassNotFoundException, IOException {
+ // read in any hidden stuff
+ s.defaultReadObject();
+
+ Class<L> listenerClass = (Class<L>) s.readObject();
+ this.listeners = this.buildListenerArray(listenerClass, 0);
+ Object o;
+ while ((o = s.readObject()) != null) {
+ this.listeners = ArrayTools.add(this.listeners, (L) o);
+ }
+ }
+
+}
diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/NameTools.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/NameTools.java
new file mode 100644
index 0000000000..dd7d65f0bf
--- /dev/null
+++ b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/NameTools.java
@@ -0,0 +1,376 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2009 Oracle. 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:
+ * Oracle - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.jpt.common.utility.internal;
+
+import java.beans.Introspector;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.SortedSet;
+import org.eclipse.jpt.common.utility.internal.iterators.ArrayIterator;
+
+/**
+ * Various helper methods for generating names.
+ */
+public final class NameTools {
+
+ /**
+ * Given a "root" name and a set of existing names, generate a unique
+ * name that is either the "root" name or some variation on the "root"
+ * name (e.g. "root2", "root3",...). The names are case-sensitive
+ * (i.e. "Root" and "root" are both allowed).
+ */
+ public static String uniqueNameFor(String rootName, Iterator<String> existingNames) {
+ return uniqueNameFor(rootName, CollectionTools.set(existingNames));
+ }
+
+ /**
+ * Given a "root" name and a set of existing names, generate a unique
+ * name that is either the "root" name or some variation on the "root"
+ * name (e.g. "root2", "root3",...). The names are case-sensitive
+ * (i.e. "Root" and "root" are both allowed).
+ */
+ public static String uniqueNameFor(String rootName, Collection<String> existingNames) {
+ return uniqueNameFor(rootName, existingNames, rootName);
+ }
+
+ /**
+ * Given a "root" name and a set of existing names, generate a unique
+ * name that is either the "root" name or some variation on the "root"
+ * name (e.g. "root2", "root3",...). The names are NOT case-sensitive
+ * (i.e. "Root" and "root" are NOT both allowed).
+ */
+ public static String uniqueNameForIgnoreCase(String rootName, Iterator<String> existingNames) {
+ return uniqueNameForIgnoreCase(rootName, CollectionTools.set(existingNames));
+ }
+
+ /**
+ * Given a "root" name and a set of existing names, generate a unique
+ * name that is either the "root" name or some variation on the "root"
+ * name (e.g. "root2", "root3",...). The names are NOT case-sensitive
+ * (i.e. "Root" and "root" are NOT both allowed).
+ */
+ public static String uniqueNameForIgnoreCase(String rootName, Collection<String> existingNames) {
+ return uniqueNameFor(rootName, convertToLowerCase(existingNames), rootName.toLowerCase());
+ }
+
+ /**
+ * use the suffixed "template" name to perform the comparisons, but RETURN
+ * the suffixed "root" name; this allows case-insensitive comparisons
+ * (i.e. the "template" name has been morphed to the same case as
+ * the "existing" names, while the "root" name has not, but the "root" name
+ * is what the client wants morphed to be unique)
+ */
+ private static String uniqueNameFor(String rootName, Collection<String> existingNames, String templateName) {
+ if ( ! existingNames.contains(templateName)) {
+ return rootName;
+ }
+ String uniqueName = templateName;
+ for (int suffix = 2; true; suffix++) {
+ if ( ! existingNames.contains(uniqueName + suffix)) {
+ return rootName.concat(String.valueOf(suffix));
+ }
+ }
+ }
+
+ /**
+ * Convert the specified collection of strings to a collection of the same
+ * strings converted to lower case.
+ */
+ private static HashSet<String> convertToLowerCase(Collection<String> strings) {
+ HashSet<String> result = new HashSet<String>(strings.size());
+ for (String string : strings) {
+ result.add(string.toLowerCase());
+ }
+ return result;
+ }
+
+ /**
+ * Build a fully-qualified name for the specified database object.
+ * Variations:
+ * catalog.schema.name
+ * catalog..name
+ * schema.name
+ * name
+ */
+ public static String buildQualifiedDatabaseObjectName(String catalog, String schema, String name) {
+ if (name == null) {
+ return null;
+ }
+ if ((catalog == null) && (schema == null)) {
+ return name;
+ }
+
+ StringBuilder sb = new StringBuilder(100);
+ if (catalog != null) {
+ sb.append(catalog);
+ sb.append('.');
+ }
+ if (schema != null) {
+ sb.append(schema);
+ }
+ sb.append('.');
+ sb.append(name);
+ return sb.toString();
+ }
+
+ /**
+ * The set of reserved words in the Java programming language.
+ * These words cannot be used as identifiers (i.e. names).
+ * http://java.sun.com/docs/books/tutorial/java/nutsandbolts/_keywords.html
+ */
+ @SuppressWarnings("nls")
+ public static final String[] JAVA_RESERVED_WORDS = new String[] {
+ "abstract",
+ "assert", // jdk 1.4
+ "boolean",
+ "break",
+ "byte",
+ "case",
+ "catch",
+ "char",
+ "class",
+ "const", // unused
+ "continue",
+ "default",
+ "do",
+ "double",
+ "else",
+ "enum", // jdk 1.5
+ "extends",
+ "false",
+ "final",
+ "finally",
+ "float",
+ "for",
+ "goto", // unused
+ "if",
+ "implements",
+ "import",
+ "instanceof",
+ "int",
+ "interface",
+ "long",
+ "native",
+ "new",
+ "null",
+ "package",
+ "private",
+ "protected",
+ "public",
+ "return",
+ "short",
+ "static",
+ "strictfp", // jdk 1.2
+ "super",
+ "switch",
+ "synchronized",
+ "this",
+ "throw",
+ "throws",
+ "transient",
+ "true",
+ "try",
+ "void",
+ "volatile",
+ "while"
+ };
+
+ /**
+ * The set of reserved words in the Java programming language.
+ * These words cannot be used as identifiers (i.e. names).
+ * http://java.sun.com/docs/books/tutorial/java/nutsandbolts/_keywords.html
+ */
+ public static final SortedSet<String> JAVA_RESERVED_WORDS_SET =
+ Collections.unmodifiableSortedSet(CollectionTools.sortedSet(JAVA_RESERVED_WORDS));
+
+ /**
+ * Return the set of Java programming language reserved words.
+ * These words cannot be used as identifiers (i.e. names).
+ * http://java.sun.com/docs/books/tutorial/java/nutsandbolts/_keywords.html
+ */
+ public static Iterator<String> javaReservedWords() {
+ return new ArrayIterator<String>(JAVA_RESERVED_WORDS);
+ }
+
+ /**
+ * Return whether the specified string consists of Java identifier
+ * characters (but may be a reserved word).
+ */
+ public static boolean stringConsistsOfJavaIdentifierCharacters(String string) {
+ if (string.length() == 0) {
+ return false;
+ }
+ return stringConsistsOfJavaIdentifierCharacters_(string.toCharArray());
+ }
+
+ /**
+ * Return whether the specified string consists of Java identifier
+ * characters (but may be a reserved word).
+ */
+ public static boolean stringConsistsOfJavaIdentifierCharacters(char[] string) {
+ if (string.length == 0) {
+ return false;
+ }
+ return stringConsistsOfJavaIdentifierCharacters_(string);
+ }
+
+ /**
+ * The specified string must not be empty.
+ */
+ private static boolean stringConsistsOfJavaIdentifierCharacters_(char[] string) {
+ if ( ! Character.isJavaIdentifierStart(string[0])) {
+ return false;
+ }
+ for (int i = string.length; i-- > 1; ) { // NB: end with 1
+ if ( ! Character.isJavaIdentifierPart(string[i])) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Return whether the specified string is a valid Java identifier.
+ */
+ public static boolean stringIsLegalJavaIdentifier(String string) {
+ return stringConsistsOfJavaIdentifierCharacters(string)
+ && ! JAVA_RESERVED_WORDS_SET.contains(string);
+ }
+
+ /**
+ * Return whether the specified string is a valid Java identifier.
+ */
+ public static boolean stringIsLegalJavaIdentifier(char[] string) {
+ return stringConsistsOfJavaIdentifierCharacters(string)
+ && ! JAVA_RESERVED_WORDS_SET.contains(new String(string));
+ }
+
+ /**
+ * Convert the specified string to a valid Java identifier
+ * by substituting an underscore '_' for any invalid characters
+ * in the string and appending an underscore '_' to the string if
+ * it is a Java reserved word.
+ */
+ public static String convertToJavaIdentifier(String string) {
+ return convertToJavaIdentifier(string, '_');
+ }
+
+ /**
+ * Convert the specified string to a valid Java identifier
+ * by substituting the specified character for any invalid characters
+ * in the string and, if necessary, appending the specified character
+ * to the string until it is not a Java reserved word.
+ */
+ public static String convertToJavaIdentifier(String string, char c) {
+ if (string.length() == 0) {
+ return string;
+ }
+ if (JAVA_RESERVED_WORDS_SET.contains(string)) {
+ // a reserved word is a valid identifier, we just need to tweak it a bit
+ checkCharIsJavaIdentifierPart(c);
+ return convertToJavaIdentifier(string + c, c);
+ }
+ char[] array = string.toCharArray();
+ return convertToJavaIdentifier_(array, c) ? new String(array) : string;
+ }
+
+ /**
+ * Convert the specified string to a valid Java identifier
+ * by substituting an underscore '_' for any invalid characters
+ * in the string and appending an underscore '_' to the string if
+ * it is a Java reserved word.
+ */
+ public static char[] convertToJavaIdentifier(char[] string) {
+ return convertToJavaIdentifier(string, '_');
+ }
+
+ /**
+ * Convert the specified string to a valid Java identifier
+ * by substituting the specified character for any invalid characters
+ * in the string and, if necessary, appending the specified character
+ * to the string until it is not a Java reserved word.
+ */
+ public static char[] convertToJavaIdentifier(char[] string, char c) {
+ if (string.length == 0) {
+ return string;
+ }
+ if (JAVA_RESERVED_WORDS_SET.contains(new String(string))) {
+ // a reserved word is a valid identifier, we just need to tweak it a bit
+ checkCharIsJavaIdentifierPart(c);
+ return convertToJavaIdentifier(ArrayTools.add(string, c), c);
+ }
+ convertToJavaIdentifier_(string, c);
+ return string;
+ }
+
+ /**
+ * The specified string must not be empty.
+ * Return whether the string was modified.
+ */
+ private static boolean convertToJavaIdentifier_(char[] string, char c) {
+ boolean mod = false;
+ if ( ! Character.isJavaIdentifierStart(string[0])) {
+ checkCharIsJavaIdentifierStart(c);
+ string[0] = c;
+ mod = true;
+ }
+ checkCharIsJavaIdentifierPart(c);
+ for (int i = string.length; i-- > 1; ) { // NB: end with 1
+ if ( ! Character.isJavaIdentifierPart(string[i])) {
+ string[i] = c;
+ mod = true;
+ }
+ }
+ return mod;
+ }
+
+ private static void checkCharIsJavaIdentifierStart(char c) {
+ if ( ! Character.isJavaIdentifierStart(c)) {
+ throw new IllegalArgumentException("invalid Java identifier start char: '" + c + '\''); //$NON-NLS-1$
+ }
+ }
+
+ private static void checkCharIsJavaIdentifierPart(char c) {
+ if ( ! Character.isJavaIdentifierPart(c)) {
+ throw new IllegalArgumentException("invalid Java identifier part char: '" + c + '\''); //$NON-NLS-1$
+ }
+ }
+
+ /**
+ * Convert the specified method name to a property name.
+ */
+ public static String convertGetterSetterMethodNameToPropertyName(String methodName) {
+ int beginIndex = 0;
+ if (methodName.startsWith("get")) { //$NON-NLS-1$
+ beginIndex = 3;
+ } else if (methodName.startsWith("set")) { //$NON-NLS-1$
+ beginIndex = 3;
+ } else if (methodName.startsWith("is")) { //$NON-NLS-1$
+ beginIndex = 2;
+ } else {
+ return methodName; // return method name unchanged?
+ }
+ return Introspector.decapitalize(methodName.substring(beginIndex));
+ }
+
+
+ // ********** constructor **********
+
+ /**
+ * Suppress default constructor, ensuring non-instantiability.
+ */
+ private NameTools() {
+ super();
+ throw new UnsupportedOperationException();
+ }
+
+}
diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/NonNullBooleanTransformer.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/NonNullBooleanTransformer.java
new file mode 100644
index 0000000000..0c9749fcd3
--- /dev/null
+++ b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/NonNullBooleanTransformer.java
@@ -0,0 +1,79 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Oracle. 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:
+ * Oracle - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.jpt.common.utility.internal;
+
+/**
+ * A <code>NonNullBooleanTransformer</code> will transform a possibly-null
+ * {@link Boolean} to a non-null {@link Boolean}:<ul>
+ * <li>When the original {@link Boolean} is <em>not</em> <code>null</code>,
+ * the transformer will return it unchanged.
+ * <li>When the original {@link Boolean} is <code>null</code>,
+ * the transformer will return its client-specified "null value"
+ * ({@link Boolean#TRUE} or {@link Boolean#FALSE}).
+ * </ul>
+ */
+public final class NonNullBooleanTransformer
+ implements Transformer<Boolean, Boolean>
+{
+ // not null
+ private final Boolean nullValue;
+
+ /**
+ * A {@link Transformer} that will return the original {@link Boolean} when
+ * it is non-<code>null</code>; otherwise the {@link Transformer} will return
+ * {@link Boolean#TRUE}.
+ */
+ public static final Transformer<Boolean, Boolean> TRUE = new NonNullBooleanTransformer(Boolean.TRUE);
+
+ /**
+ * A {@link Transformer} that will return the original {@link Boolean} when
+ * it is non-<code>null</code>; otherwise the {@link Transformer} will return
+ * {@link Boolean#FALSE}.
+ */
+ public static final Transformer<Boolean, Boolean> FALSE = new NonNullBooleanTransformer(Boolean.FALSE);
+
+ /**
+ * Return a transformer that will return the specified value if the original
+ * value is <code>null</code>. Throw a {@link NullPointerException} if the
+ * specified value is <code>null</code>.
+ */
+ public Transformer<Boolean, Boolean> valueOf(Boolean b) {
+ return valueOf(b.booleanValue());
+ }
+
+ /**
+ * Return a transformer that will return the {@link Boolean} corresponding
+ * to the specified value if the original value is <code>null</code>.
+ */
+ public Transformer<Boolean, Boolean> valueOf(boolean b) {
+ return b ? TRUE : FALSE;
+ }
+
+ /**
+ * Ensure only 2 constant versions.
+ */
+ private NonNullBooleanTransformer(Boolean nullValue) {
+ super();
+ if (nullValue == null) {
+ throw new NullPointerException();
+ }
+ this.nullValue = nullValue;
+ }
+
+ public Boolean transform(Boolean b) {
+ return (b != null) ? b : this.nullValue;
+ }
+
+ @Override
+ public String toString() {
+ return StringTools.buildToStringFor(this, this.nullValue);
+ }
+
+}
diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/NotBooleanTransformer.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/NotBooleanTransformer.java
new file mode 100644
index 0000000000..c234ac7f73
--- /dev/null
+++ b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/NotBooleanTransformer.java
@@ -0,0 +1,56 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Oracle. 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:
+ * Oracle - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.jpt.common.utility.internal;
+
+/**
+ * A <code>NotBooleanTransformer</code> will transform a
+ * {@link Boolean} to its NOT value:<ul>
+ * <li>If the original {@link Boolean} is {@link Boolean#TRUE},
+ * the transformer will return {@link Boolean#FALSE}.
+ * <li>If the original {@link Boolean} is {@link Boolean#FALSE},
+ * the transformer will return {@link Boolean#TRUE}.
+ * <li>If the original {@link Boolean} is <code>null</code>,
+ * the transformer will return <code>null</code>.
+ * </ul>
+ */
+public class NotBooleanTransformer
+ implements BidiTransformer<Boolean, Boolean>
+{
+ public static final BidiTransformer<Boolean, Boolean> INSTANCE = new NotBooleanTransformer();
+
+ public static BidiTransformer<Boolean, Boolean> instance() {
+ return INSTANCE;
+ }
+
+ // ensure single instance
+ private NotBooleanTransformer() {
+ super();
+ }
+
+ public Boolean transform(Boolean b) {
+ return (b == null) ? null : BooleanTools.not(b);
+ }
+
+ public Boolean reverseTransform(Boolean b) {
+ return (b == null) ? null : BooleanTools.not(b);
+ }
+
+ @Override
+ public String toString() {
+ return this.getClass().getSimpleName();
+ }
+
+ private static final long serialVersionUID = 1L;
+ private Object readResolve() {
+ // replace this object with the singleton
+ return INSTANCE;
+ }
+
+}
diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/NotNullFilter.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/NotNullFilter.java
new file mode 100644
index 0000000000..c1e27e45b9
--- /dev/null
+++ b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/NotNullFilter.java
@@ -0,0 +1,51 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Oracle. 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:
+ * Oracle - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.jpt.common.utility.internal;
+
+import java.io.Serializable;
+
+import org.eclipse.jpt.common.utility.Filter;
+
+/**
+ * This filter accepts only non-null objects.
+ */
+public final class NotNullFilter<T>
+ implements Filter<T>, Serializable
+{
+ @SuppressWarnings("rawtypes")
+ public static final Filter INSTANCE = new NotNullFilter();
+
+ @SuppressWarnings("unchecked")
+ public static <R> Filter<R> instance() {
+ return INSTANCE;
+ }
+
+ // ensure single instance
+ private NotNullFilter() {
+ super();
+ }
+
+ // accept only non-null objects
+ public boolean accept(T o) {
+ return o != null;
+ }
+
+ @Override
+ public String toString() {
+ return this.getClass().getSimpleName();
+ }
+
+ private static final long serialVersionUID = 1L;
+ private Object readResolve() {
+ // replace this object with the singleton
+ return INSTANCE;
+ }
+
+}
diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/NullList.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/NullList.java
new file mode 100644
index 0000000000..547a882c58
--- /dev/null
+++ b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/NullList.java
@@ -0,0 +1,153 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2010 Oracle. 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:
+ * Oracle - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.jpt.common.utility.internal;
+
+import java.io.Serializable;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.ListIterator;
+import org.eclipse.jpt.common.utility.internal.iterators.EmptyIterator;
+import org.eclipse.jpt.common.utility.internal.iterators.EmptyListIterator;
+
+/**
+ * A "null" list is a bit different from an "empty" list: it allows clients to
+ * add/remove elements to/from it but never changes. This is useful
+ * for passing to methods that require a "collecting parameter" but the
+ * client will ignore the resulting "collection".
+ */
+public final class NullList<E>
+ implements List<E>, Serializable
+{
+
+ // singleton
+ @SuppressWarnings("rawtypes")
+ private static final NullList INSTANCE = new NullList();
+
+ /**
+ * Return the singleton.
+ */
+ @SuppressWarnings("unchecked")
+ public static <E> List<E> instance() {
+ return INSTANCE;
+ }
+
+ /**
+ * Ensure single instance.
+ */
+ private NullList() {
+ super();
+ }
+
+ public boolean add(E o) {
+ return false; // the list did not change
+ }
+
+ public void add(int index, E element) {
+ // ignore
+ }
+
+ public boolean addAll(Collection<? extends E> c) {
+ return false; // the list did not change
+ }
+
+ public boolean addAll(int index, Collection<? extends E> c) {
+ return false; // the list did not change
+ }
+
+ public void clear() {
+ // ignore
+ }
+
+ public boolean contains(Object o) {
+ return false;
+ }
+
+ public boolean containsAll(Collection<?> c) {
+ return c.isEmpty();
+ }
+
+ public E get(int index) {
+ throw new IndexOutOfBoundsException("Index: " + index + ", Size: 0"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ public int indexOf(Object o) {
+ return -1;
+ }
+
+ public boolean isEmpty() {
+ return true;
+ }
+
+ public Iterator<E> iterator() {
+ return EmptyIterator.instance();
+ }
+
+ public int lastIndexOf(Object o) {
+ return -1;
+ }
+
+ public ListIterator<E> listIterator() {
+ return EmptyListIterator.instance();
+ }
+
+ public ListIterator<E> listIterator(int index) {
+ return EmptyListIterator.instance();
+ }
+
+ public boolean remove(Object o) {
+ return false; // the list did not change
+ }
+
+ public E remove(int index) {
+ throw new IndexOutOfBoundsException("Index: " + index + ", Size: 0"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ public boolean removeAll(Collection<?> c) {
+ return false; // the list did not change
+ }
+
+ public boolean retainAll(Collection<?> c) {
+ return false; // the list did not change
+ }
+
+ public E set(int index, E element) {
+ throw new IndexOutOfBoundsException("Index: " + index + ", Size: 0"); //$NON-NLS-1$ //$NON-NLS-2$
+ }
+
+ public int size() {
+ return 0;
+ }
+
+ public List<E> subList(int fromIndex, int toIndex) {
+ return this;
+ }
+
+ private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];
+ public Object[] toArray() {
+ return EMPTY_OBJECT_ARRAY;
+ }
+
+ public <T> T[] toArray(T[] a) {
+ return a;
+ }
+
+ @Override
+ public String toString() {
+ return StringTools.buildToStringFor(this);
+ }
+
+ private static final long serialVersionUID = 1L;
+ private Object readResolve() {
+ // replace this object with the singleton
+ return INSTANCE;
+ }
+
+}
diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/Queue.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/Queue.java
new file mode 100644
index 0000000000..9cc4bc5ce3
--- /dev/null
+++ b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/Queue.java
@@ -0,0 +1,75 @@
+/*******************************************************************************
+ * Copyright (c) 2009, 2010 Oracle. 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:
+ * Oracle - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.jpt.common.utility.internal;
+
+import java.io.Serializable;
+import java.util.NoSuchElementException;
+
+/**
+ * Interface defining the classic queue behavior,
+ * without the backdoors allowed by {@link java.util.Queue}.
+ *
+ * @param <E> the type of elements contained by the queue
+ */
+public interface Queue<E> {
+
+ /**
+ * "Enqueue" the specified item to the tail of the queue.
+ */
+ void enqueue(E o);
+
+ /**
+ * "Dequeue" an item from the head of the queue.
+ */
+ E dequeue();
+
+ /**
+ * Return the item on the head of the queue
+ * without removing it from the queue.
+ */
+ E peek();
+
+ /**
+ * Return whether the queue is empty.
+ */
+ boolean isEmpty();
+
+
+ final class Empty<E> implements Queue<E>, Serializable {
+ @SuppressWarnings("rawtypes")
+ public static final Queue INSTANCE = new Empty();
+ @SuppressWarnings("unchecked")
+ public static <T> Queue<T> instance() {
+ return INSTANCE;
+ }
+ // ensure single instance
+ private Empty() {
+ super();
+ }
+ public void enqueue(E o) {
+ throw new UnsupportedOperationException();
+ }
+ public E dequeue() {
+ throw new NoSuchElementException();
+ }
+ public E peek() {
+ throw new NoSuchElementException();
+ }
+ public boolean isEmpty() {
+ return true;
+ }
+ private static final long serialVersionUID = 1L;
+ private Object readResolve() {
+ // replace this object with the singleton
+ return INSTANCE;
+ }
+ }
+
+}
diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/Range.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/Range.java
new file mode 100644
index 0000000000..bc66046c0a
--- /dev/null
+++ b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/Range.java
@@ -0,0 +1,87 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2008 Oracle. 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:
+ * Oracle - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.jpt.common.utility.internal;
+
+import java.io.Serializable;
+
+/**
+ * This simple container class simply puts a bit of semantics
+ * around a pair of numbers.
+ */
+public class Range
+ implements Cloneable, Serializable
+{
+ /** The starting index of the range. */
+ public final int start;
+
+ /** The ending index of the range. */
+ public final int end;
+
+ /**
+ * The size can be negative if the ending index
+ * is less than the starting index.
+ */
+ public final int size;
+
+ private static final long serialVersionUID = 1L;
+
+
+ /**
+ * Construct with the specified start and end,
+ * both of which are immutable.
+ */
+ public Range(int start, int end) {
+ super();
+ this.start = start;
+ this.end = end;
+ this.size = end - start + 1;
+ }
+
+ /**
+ * Return whether the range includes the specified
+ * index.
+ */
+ public boolean includes(int index) {
+ return (this.start <= index) && (index <= this.end);
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if ( ! (o instanceof Range)) {
+ return false;
+ }
+ Range otherRange = (Range) o;
+ return (this.start == otherRange.start)
+ && (this.end == otherRange.end);
+ }
+
+ @Override
+ public int hashCode() {
+ return this.start ^ this.end;
+ }
+
+ @Override
+ public Range clone() {
+ try {
+ return (Range) super.clone();
+ } catch (CloneNotSupportedException ex) {
+ throw new InternalError();
+ }
+ }
+
+ @Override
+ public String toString() {
+ return '[' + this.start + ", " + this.end + ']'; //$NON-NLS-1$
+ }
+
+}
diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/ReadOnlyBooleanReference.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/ReadOnlyBooleanReference.java
new file mode 100644
index 0000000000..0d2c56a3bf
--- /dev/null
+++ b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/ReadOnlyBooleanReference.java
@@ -0,0 +1,46 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Oracle. 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:
+ * Oracle - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.jpt.common.utility.internal;
+
+/**
+ * Interface for a container for holding a <code>boolean</code> that cannot be
+ * changed by clients.
+ */
+public interface ReadOnlyBooleanReference
+{
+ /**
+ * Return the current <code>boolean</code> value.
+ */
+ boolean getValue();
+
+ /**
+ * Return whether the current <code>boolean</code> value is equal to the
+ * specified value.
+ */
+ boolean is(boolean value);
+
+ /**
+ * Return whether the current <code>boolean</code> value is not equal to
+ * the specified value.
+ */
+ boolean isNot(boolean value);
+
+ /**
+ * Return whether the current <code>boolean</code> value is
+ * <code>true</code>.
+ */
+ boolean isTrue();
+
+ /**
+ * Return whether the current <code>boolean</code> value is
+ * <code>false</code>.
+ */
+ boolean isFalse();
+}
diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/ReadOnlyIntReference.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/ReadOnlyIntReference.java
new file mode 100644
index 0000000000..50e5154669
--- /dev/null
+++ b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/ReadOnlyIntReference.java
@@ -0,0 +1,145 @@
+/*******************************************************************************
+ * Copyright (c) 2010 Oracle. 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:
+ * Oracle - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.jpt.common.utility.internal;
+
+/**
+ * Interface for a container for holding an <code>int</code> that cannot be
+ * changed by clients.
+ */
+public interface ReadOnlyIntReference
+ extends Comparable<ReadOnlyIntReference>
+{
+ /**
+ * Return the current <code>int</code> value.
+ */
+ int getValue();
+
+ /**
+ * Return whether the current <code>int</code> value is equal to the
+ * specified value.
+ */
+ boolean equals(int v);
+
+ /**
+ * Return whether the current <code>int</code> value is not equal to
+ * the specified value.
+ */
+ boolean notEqual(int v);
+
+ /**
+ * Return whether the current <code>int</code> value is zero.
+ */
+ boolean isZero();
+
+ /**
+ * Return whether the current <code>int</code> value is not zero.
+ */
+ boolean isNotZero();
+
+ /**
+ * Return whether the current <code>int</code> value is greater than
+ * the specified value.
+ */
+ boolean isGreaterThan(int v);
+
+ /**
+ * Return whether the current <code>int</code> value is greater than
+ * or equal to the specified value.
+ */
+ boolean isGreaterThanOrEqual(int v);
+
+ /**
+ * Return whether the current <code>int</code> value is less than
+ * the specified value.
+ */
+ boolean isLessThan(int v);
+
+ /**
+ * Return whether the current <code>int</code> value is less than
+ * or equal to the specified value.
+ */
+ boolean isLessThanOrEqual(int v);
+
+ /**
+ * Return whether the current <code>int</code> value is positive.
+ */
+ boolean isPositive();
+
+ /**
+ * Return whether the current <code>int</code> value is not positive
+ * (i.e. negative or zero).
+ */
+ boolean isNotPositive();
+
+ /**
+ * Return whether the current <code>int</code> value is negative.
+ */
+ boolean isNegative();
+
+ /**
+ * Return whether the current <code>int</code> value is not negative
+ * (i.e. zero or positive).
+ */
+ boolean isNotNegative();
+
+ /**
+ * Return the absolute value of the current <code>int</code> value.
+ */
+ int abs();
+
+ /**
+ * Return the negative value of the current <code>int</code> value.
+ */
+ int neg();
+
+ /**
+ * Return the current <code>int</code> value plus the specified value.
+ */
+ int add(int v);
+
+ /**
+ * Return current <code>int</code> value minus the specified value.
+ */
+ int subtract(int v);
+
+ /**
+ * Return current <code>int</code> value multiplied by the specified value.
+ */
+ int multiply(int v);
+
+ /**
+ * Return current <code>int</code> value divided by the specified value.
+ */
+ int divide(int v);
+
+ /**
+ * Return the remainder of the current <code>int</code> value divided by
+ * the specified value.
+ */
+ int remainder(int v);
+
+ /**
+ * Return the minimum of the current <code>int</code> value and
+ * the specified value.
+ */
+ int min(int v);
+
+ /**
+ * Return the maximum of the current <code>int</code> value and
+ * the specified value.
+ */
+ int max(int v);
+
+ /**
+ * Return the current <code>int</code> value raised to the power
+ * of the specified value.
+ */
+ double pow(int v);
+}
diff --git a/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/ReflectionTools.java b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/ReflectionTools.java
new file mode 100644
index 0000000000..6fb33c7f8c
--- /dev/null
+++ b/common/plugins/org.eclipse.jpt.common.utility/src/org/eclipse/jpt/common/utility/internal/ReflectionTools.java
@@ -0,0 +1,1544 @@
+/*******************************************************************************
+ * Copyright (c) 2005, 2010 Oracle. 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:
+ * Oracle - initial API and implementation
+ ******************************************************************************/
+package org.eclipse.jpt.common.utility.internal;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+
+import org.eclipse.jpt.common.utility.internal.iterables.ArrayIterable;
+
+/**
+ * Convenience methods related to the <code>java.lang.reflect</code> package.
+ * These methods provide shortcuts for manipulating objects via
+ * reflection; particularly when dealing with fields and/or methods that
+ * are not publicly accessible or are inherited.
+ * <p>
+ * In most cases, all exceptions are handled and wrapped in
+ * {@link java.lang.RuntimeException}s; so these methods should
+ * be used when there should be no problems using reflection (i.e.
+ * the referenced members are presumably present etc.).
+ * <p>
+ * There are also a number of methods whose names
+ * end with an underscore. These methods declare the expected checked
+ * exceptions (e.g. {@link NoSuchMethodException}, {@link NoSuchFieldException}).
+ * These methods can be used to probe
+ * for methods, fields, etc. that should be present but might not be.
+ */
+public final class ReflectionTools {
+
+ public static final Class<?>[] ZERO_PARAMETER_TYPES = new Class[0];
+ public static final Object[] ZERO_ARGUMENTS = new Object[0];
+ private static final String CR = StringTools.CR;
+
+ public static final Class<?> VOID_CLASS = void.class;
+ public static final Class<java.lang.Void> VOID_WRAPPER_CLASS = java.lang.Void.class;
+
+
+ // ********** fields **********
+
+ /**
+ * Get a field value, given the containing object and field name.
+ * Return its result.
+ * Useful for accessing private, package, or protected fields.
+ * <p>
+ * <code>Object.getFieldValue(String fieldName)</code>
+ */
+ public static Object getFieldValue(Object object, String fieldName) {
+ try {
+ return getFieldValue_(object, fieldName);
+ } catch (NoSuchFieldException ex) {
+ throw new RuntimeException(ex + CR + buildFullyQualifiedFieldName(object, fieldName), ex);
+ } catch (IllegalAccessException ex) {
+ throw new RuntimeException(ex + CR + buildFullyQualifiedFieldName(object, fieldName), ex);
+ }
+ }
+
+ /**
+ * Get a field value, given the containing object and field name.
+ * Return its result.
+ * Useful for accessing private, package, or protected fields.
+ * <p>
+ * <code>Object.getFieldValue(String fieldName)</code>
+ */
+ public static Object getFieldValue_(Object object, String fieldName)
+ throws NoSuchFieldException, IllegalAccessException
+ {
+ return getField_(object, fieldName).get(object);
+ }
+
+ /**
+ * Get a static field value, given the containing class and field name.
+ * Return its result.
+ * Useful for accessing private, package, or protected fields.
+ * <p>
+ * <code>Class.getStaticFieldValue(String fieldName)</code>
+ */
+ public static Object getStaticFieldValue(Class<?> javaClass, String fieldName) {
+ try {
+ return getStaticFieldValue_(javaClass, fieldName);
+ } catch (NoSuchFieldException ex) {
+ throw new RuntimeException(ex + CR + buildFullyQualifiedFieldName(javaClass, fieldName), ex);
+ } catch (IllegalAccessException ex) {
+ throw new RuntimeException(ex + CR + buildFullyQualifiedFieldName(javaClass, fieldName), ex);
+ }
+ }
+
+ /**
+ * Get a static field value, given the containing class and field name.
+ * Return its result.
+ * Useful for accessing private, package, or protected fields.
+ * <p>
+ * <code>Class.getStaticFieldValue(String fieldName)</code>
+ */
+ public static Object getStaticFieldValue_(Class<?> javaClass, String fieldName)
+ throws NoSuchFieldException, IllegalAccessException
+ {
+ return getField_(javaClass, fieldName).get(null);
+ }
+
+ /**
+ * Set a field value, given the containing object, field name, and new value.
+ * Useful for accessing private, package, or protected fields.
+ * <p>
+ * <code>Object.setFieldValue(String fieldName, Object value)</code>
+ */
+ public static void setFieldValue(Object object, String fieldName, Object value) {
+ try {
+ setFieldValue_(object, fieldName, value);
+ } catch (NoSuchFieldException ex) {
+ throw new RuntimeException(ex + CR + buildFullyQualifiedFieldName(object, fieldName), ex);
+ } catch (IllegalAccessException ex) {
+ throw new RuntimeException(ex + CR + buildFullyQualifiedFieldName(object, fieldName), ex);
+ }
+ }
+
+ /**
+ * Set a field value, given the containing object, field name, and new value.
+ * Useful for accessing private, package, or protected fields.
+ * <p>
+ * <code>Object.setFieldValue(String fieldName, Object value)</code>
+ */
+ public static void setFieldValue_(Object object, String fieldName, Object value)
+ throws NoSuchFieldException, IllegalAccessException
+ {
+ getField_(object, fieldName).set(object, value);
+ }
+
+ /**
+ * Set a static field value, given the containing class, field name, and new value.
+ * Useful for accessing private, package, or protected fields.
+ * <p>
+ * <code>Class.setStaticFieldValue(String fieldName, Object value)</code>
+ */
+ public static void setStaticFieldValue(Class<?> javaClass, String fieldName, Object value) {
+ try {
+ setStaticFieldValue_(javaClass, fieldName, value);
+ } catch (NoSuchFieldException ex) {
+ throw new RuntimeException(ex + CR + buildFullyQualifiedFieldName(javaClass, fieldName), ex);
+ } catch (IllegalAccessException ex) {
+ throw new RuntimeException(ex + CR + buildFullyQualifiedFieldName(javaClass, fieldName), ex);
+ }
+ }
+
+ /**
+ * Set a static field value, given the containing class, field name, and new value.
+ * Useful for accessing private, package, or protected fields.
+ * <p>
+ * <code>Class.setStaticFieldValue(String fieldName, Object value)</code>
+ */
+ public static void setStaticFieldValue_(Class<?> javaClass, String fieldName, Object value)
+ throws NoSuchFieldException, IllegalAccessException
+ {
+ getField_(javaClass, fieldName).set(null, value);
+ }
+
+ /**
+ * Convenience method.
+ * Return a field for the specified object and field name.
+ * If the object's class does not directly
+ * define the field, look for it in the class's superclasses.
+ * Make any private/package/protected field accessible.
+ * <p>
+ * <code>Object.getField(String fieldName)</code>
+ */
+ public static Field getField(Object object, String fieldName) {
+ try {
+ return getField_(object, fieldName);
+ } catch (NoSuchFieldException ex) {
+ throw new RuntimeException(ex + CR + buildFullyQualifiedFieldName(object, fieldName), ex);
+ }
+ }
+
+ /**
+ * Convenience method.
+ * Return a field for the specified object and field name.
+ * If the object's class does not directly
+ * define the field, look for it in the class's superclasses.
+ * Make any private/package/protected field accessible.
+ * <p>
+ * <code>Object.getField(String fieldName)</code>
+ */
+ public static Field getField_(Object object, String fieldName)
+ throws NoSuchFieldException
+ {
+ return getField_(object.getClass(), fieldName);
+ }
+
+ /**
+ * Return a field for the specified class and field name.
+ * If the class does not directly
+ * define the field, look for it in the class's superclasses.
+ * Make any private/package/protected field accessible.
+ */
+ public static Field getField(Class<?> javaClass, String fieldName) {
+ try {
+ return getField_(javaClass, fieldName);
+ } catch (NoSuchFieldException ex) {
+ throw new RuntimeException(ex + CR + buildFullyQualifiedFieldName(javaClass, fieldName), ex);
+ }
+ }
+
+ /**
+ * Return a field for the specified class and field name.
+ * If the class does not directly
+ * define the field, look for it in the class's superclasses.
+ * Make any private/package/protected field accessible.
+ */
+ public static Field getField_(Class<?> javaClass, String fieldName)
+ throws NoSuchFieldException
+ {
+ Field field = null;
+ try {
+ field = javaClass.getDeclaredField(fieldName);
+ } catch (NoSuchFieldException ex) {
+ Class<?> superclass = javaClass.getSuperclass();
+ if (superclass == null) {
+ throw ex;
+ }
+ return getField_(superclass, fieldName); // recurse
+ }
+ field.setAccessible(true);
+ return field;
+ }
+
+ /**
+ * Return all the fields for the
+ * specified class, including inherited fields.
+ * Make any private/package/protected fields accessible.
+ * <p>
+ * <code>Class.getAllFields()</code>
+ */
+ public static Iterable<Field> getAllFields(Class<?> javaClass) {
+ ArrayList<Field> fields = new ArrayList<Field>();
+ for (Class<?> tempClass = javaClass; tempClass != null; tempClass = tempClass.getSuperclass()) {
+ addDeclaredFieldsTo(tempClass, fields);
+ }
+ return fields;
+ }
+
+ /*
+ * Add the declared fields for the specified class
+ * to the specified list.
+ */
+ private static void addDeclaredFieldsTo(Class<?> javaClass, ArrayList<Field> fields) {
+ for (Field field : getDeclaredFields(javaClass)) {
+ fields.add(field);
+ }
+ }
+
+ /**
+ * Return the declared fields for the specified class.
+ * Make any private/package/protected fields accessible.
+ * <p>
+ * <code>Class.getAccessibleDeclaredFields()</code>
+ */
+ public static Iterable<Field> getDeclaredFields(Class<?> javaClass) {
+ Field[] fields = javaClass.getDeclaredFields();
+ for (Field field : fields) {
+ field.setAccessible(true);
+ }
+ return new ArrayIterable<Field>(fields);
+ }
+
+
+ // ********** methods **********
+
+ /**
+ * Convenience method.
+ * Execute a zero-argument method, given the receiver and method name.
+ * Return its result.
+ * Useful for invoking private, package, or protected methods.
+ * <p>
+ * <code>Object.execute(String methodName)</code>
+ */
+ public static Object executeMethod(Object receiver, String methodName) {
+ return executeMethod(receiver, methodName, ZERO_PARAMETER_TYPES, ZERO_ARGUMENTS);
+ }
+
+ /**
+ * Convenience method.
+ * Execute a one-argument method, given the receiver,
+ * method name, parameter type, and argument.
+ * Return its result.
+ * Useful for invoking private, package, or protected methods.
+ * <p>
+ * <code>Object.execute(String methodName, Class<?> parameterType, Object argument)</code>
+ */
+ public static Object executeMethod(Object receiver, String methodName, Class<?> parameterType, Object argument) {
+ return executeMethod(receiver, methodName, new Class[] {parameterType}, new Object[] {argument});
+ }
+
+ /**
+ * Execute a method, given the receiver,
+ * method name, parameter types, and arguments.
+ * Return its result.
+ * Useful for invoking private, package, or protected methods.
+ * <p>
+ * <code>Object.execute(String methodName, Class<?>[] parameterTypes, Object[] arguments)</code>
+ */
+ public static Object executeMethod(Object receiver, String methodName, Class<?>[] parameterTypes, Object[] arguments) {
+ return executeMethod(getMethod(receiver, methodName, parameterTypes), receiver, arguments);
+ }
+
+ /**
+ * Execute the specified method, given the receiver and arguments.
+ * Return its result.
+ * Useful for invoking cached methods.
+ */
+ public static Object executeMethod(Method method, Object receiver, Object[] arguments) {
+ try {
+ return method.invoke(receiver, arguments);
+ } catch (IllegalAccessException ex) {
+ throw new RuntimeException(ex + CR + method, ex);
+ } catch (InvocationTargetException ex) {
+ throw new RuntimeException(method + CR + ex.getTargetException(), ex);
+ }
+ }
+
+ /**
+ * Convenience method.
+ * Execute a zero-argument method,
+ * given the receiver and method name.
+ * Return its result.
+ * Throw an exception if the method is not defined.
+ * Useful for invoking private, package, or protected methods.
+ * <p>
+ * <code>Object.execute(String methodName)</code>
+ */
+ public static Object executeMethod_(Object receiver, String methodName)
+ throws NoSuchMethodException, IllegalAccessException, InvocationTargetException
+ {
+ return executeMethod_(receiver, methodName, ZERO_PARAMETER_TYPES, ZERO_ARGUMENTS);
+ }
+
+ /**
+ * Convenience method.
+ * Execute a method, given the receiver,
+ * method name, parameter type, and argument.
+ * Return its result.
+ * Throw an exception if the method is not defined.
+ * Useful for invoking private, package, or protected methods.
+ * <p>
+ * <code>Object.execute(String methodName, Class<?> parameterType, Object argument)</code>
+ */
+ public static Object executeMethod_(Object receiver, String methodName, Class<?> parameterType, Object argument)
+ throws NoSuchMethodException, IllegalAccessException, InvocationTargetException
+ {
+ return executeMethod_(receiver, methodName, new Class[] {parameterType}, new Object[] {argument});
+ }
+
+ /**
+ * Execute a method, given the receiver,
+ * method name, parameter types, and arguments.
+ * Return its result.
+ * Throw an exception if the method is not defined.
+ * Useful for invoking private, package, or protected methods.
+ * <p>
+ * <code>Object.execute(String methodName, Class<?>[] parameterTypes, Object[] arguments)</code>
+ */
+ public static Object executeMethod_(Object receiver, String methodName, Class<?>[] parameterTypes, Object[] arguments)
+ throws NoSuchMethodException, IllegalAccessException, InvocationTargetException
+ {
+ return getMethod_(receiver, methodName, parameterTypes).invoke(receiver, arguments);
+ }
+
+ /**
+ * Convenience method.
+ * Execute a zero-argument static method,
+ * given the class and method name.
+ * Return its result.
+ * Useful for invoking private, package, or protected methods.
+ * <p>
+ * <code>Class.executeStaticMethod(String methodName)</code>
+ */
+ public static Object executeStaticMethod(Class<?> javaClass, String methodName) {
+ return executeStaticMethod(javaClass, methodName, ZERO_PARAMETER_TYPES, ZERO_ARGUMENTS);
+ }
+
+ /**
+ * Convenience method.
+ * Execute a static method, given the class,
+ * method name, parameter type, and argument.
+ * Return its result.
+ * Useful for invoking private, package, or protected methods.
+ * <p>
+ * <code>Class.executeStaticMethod(String methodName, Class<?> parameterType, Object argument)</code>
+ */
+ public static Object executeStaticMethod(Class<?> javaClass, String methodName, Class<?> parameterType, Object argument) {
+ return executeStaticMethod(javaClass, methodName, new Class[] {parameterType}, new Object[] {argument});
+ }
+
+ /**
+ * Execute a static method, given the class,
+ * method name, parameter types, and arguments.
+ * Return its result.
+ * Useful for invoking private, package, or protected methods.
+ * <p>
+ * <code>Class.executeStaticMethod(String methodName, Class<?>[] parameterTypes, Object[] arguments)</code>
+ */
+ public static Object executeStaticMethod(Class<?> javaClass, String methodName, Class<?>[] parameterTypes, Object[] arguments) {
+ try {
+ return executeStaticMethod_(javaClass, methodName, parameterTypes, arguments);
+ } catch (NoSuchMethodException ex) {
+ throw new RuntimeException(ex + CR + buildFullyQualifiedMethodSignature(javaClass, methodName, parameterTypes), ex);
+ } catch (IllegalAccessException ex) {
+ throw new RuntimeException(ex + CR + buildFullyQualifiedMethodSignature(javaClass, methodName, parameterTypes), ex);
+ } catch (InvocationTargetException ex) {
+ throw new RuntimeException(buildFullyQualifiedMethodSignature(javaClass, methodName, parameterTypes) + CR + ex.getTargetException(), ex);
+ }
+ }
+
+ /**
+ * Convenience method.
+ * Execute a zero-argument static method,
+ * given the class and method name.
+ * Return its result.
+ * Useful for invoking private, package, or protected methods.
+ * <p>
+ * <code>Class.executeStaticMethod(String methodName)</code>
+ */
+ public static Object executeStaticMethod_(Class<?> javaClass, String methodName)
+ throws NoSuchMethodException, IllegalAccessException, InvocationTargetException
+ {
+ return executeStaticMethod_(javaClass, methodName, ZERO_PARAMETER_TYPES, ZERO_ARGUMENTS);
+ }
+
+ /**
+ * Convenience method.
+ * Execute a static method, given the class,
+ * method name, parameter type, and argument.
+ * Return its result.
+ * Useful for invoking private, package, or protected methods.
+ * <p>
+ * <code>Class.executeStaticMethod(String methodName, Class<?> parameterType, Object argument)</code>
+ */
+ public static Object executeStaticMethod_(Class<?> javaClass, String methodName, Class<?> parameterType, Object argument)
+ throws NoSuchMethodException, IllegalAccessException, InvocationTargetException
+ {
+ return executeStaticMethod_(javaClass, methodName, new Class[] {parameterType}, new Object[] {argument});
+ }
+
+ /**
+ * Execute a static method, given the class,
+ * method name, parameter types, and arguments.
+ * Return its result.
+ * Useful for invoking private, package, or protected methods.
+ * <p>
+ * <code>Class.executeStaticMethod(String methodName, Class<?>[] parameterTypes, Object[] arguments)</code>
+ */
+ public static Object executeStaticMethod_(Class<?> javaClass, String methodName, Class<?>[] parameterTypes, Object[] arguments)
+ throws NoSuchMethodException, IllegalAccessException, InvocationTargetException
+ {
+ return getStaticMethod_(javaClass, methodName, parameterTypes).invoke(null, arguments);
+ }
+
+ /**
+ * Convenience method.
+ * Return a zero-argument method for the specified class
+ * and method name. If the class does not directly
+ * implement the method, look for it in the class's superclasses.
+ * Make any private/package/protected method accessible.
+ */