From 07656f129cf492c2cadf371c047fdccc60bd06af Mon Sep 17 00:00:00 2001 From: tms08012 Date: Wed, 13 Jun 2012 18:23:03 -0400 Subject: [PATCH] initial, compiles, has class diagram --- ApacheCommonsLang/.classpath | 10 + ApacheCommonsLang/.project | 17 + .../.settings/org.eclipse.jdt.core.prefs | 11 + ApacheCommonsLang/assembly/bin.xml | 44 + ApacheCommonsLang/assembly/src.xml | 43 + ApacheCommonsLang/changes/changes.xml | 587 ++ ApacheCommonsLang/changes/release-notes.vm | 138 + .../main/resources/templates/release-notes.vm | 136 + .../apache/commons/lang3/AnnotationUtils.java | 371 + .../commons/lang3/AnnotationUtilsTest.java | 508 ++ .../org/apache/commons/lang3/ArrayUtils.java | 5796 +++++++++++++++ .../commons/lang3/ArrayUtilsAddTest.java | 610 ++ .../lang3/ArrayUtilsRemoveMultipleTest.java | 1049 +++ .../commons/lang3/ArrayUtilsRemoveTest.java | 487 ++ .../apache/commons/lang3/ArrayUtilsTest.java | 2922 ++++++++ .../org/apache/commons/lang3/BitField.java | 283 + .../apache/commons/lang3/BitFieldTest.java | 251 + .../apache/commons/lang3/BooleanUtils.java | 1082 +++ .../commons/lang3/BooleanUtilsTest.java | 984 +++ .../apache/commons/lang3/CharEncoding.java | 105 + .../commons/lang3/CharEncodingTest.java | 103 + .../org/apache/commons/lang3/CharRange.java | 360 + .../apache/commons/lang3/CharRangeTest.java | 383 + .../commons/lang3/CharSequenceUtils.java | 197 + .../commons/lang3/CharSequenceUtilsTest.java | 75 + .../org/apache/commons/lang3/CharSet.java | 278 + .../org/apache/commons/lang3/CharSetTest.java | 454 ++ .../apache/commons/lang3/CharSetUtils.java | 217 + .../commons/lang3/CharSetUtilsTest.java | 204 + .../org/apache/commons/lang3/CharUtils.java | 539 ++ .../commons/lang3/CharUtilsPerfRun.java | 162 + .../apache/commons/lang3/CharUtilsTest.java | 357 + .../org/apache/commons/lang3/ClassUtils.java | 1132 +++ .../apache/commons/lang3/ClassUtilsTest.java | 1165 +++ .../org/apache/commons/lang3/EnumUtils.java | 208 + .../apache/commons/lang3/EnumUtilsTest.java | 201 + .../org/apache/commons/lang3/JavaVersion.java | 168 + .../apache/commons/lang3/JavaVersionTest.java | 68 + .../org/apache/commons/lang3/LocaleUtils.java | 297 + .../apache/commons/lang3/LocaleUtilsTest.java | 488 ++ .../org/apache/commons/lang3/ObjectUtils.java | 608 ++ .../apache/commons/lang3/ObjectUtilsTest.java | 521 ++ .../commons/lang3/RandomStringUtils.java | 335 + .../commons/lang3/RandomStringUtilsTest.java | 348 + .../org/apache/commons/lang3/Range.java | 494 ++ .../org/apache/commons/lang3/RangeTest.java | 380 + .../commons/lang3/SerializationException.java | 78 + .../commons/lang3/SerializationUtils.java | 322 + .../commons/lang3/SerializationUtilsTest.java | 425 ++ .../commons/lang3/StringEscapeUtils.java | 585 ++ .../commons/lang3/StringEscapeUtilsTest.java | 514 ++ .../org/apache/commons/lang3/StringUtils.java | 6467 +++++++++++++++++ .../lang3/StringUtilsEqualsIndexOfTest.java | 939 +++ .../commons/lang3/StringUtilsIsTest.java | 164 + .../lang3/StringUtilsStartsEndsWithTest.java | 167 + .../lang3/StringUtilsSubstringTest.java | 313 + .../apache/commons/lang3/StringUtilsTest.java | 2117 ++++++ .../lang3/StringUtilsTrimEmptyTest.java | 283 + .../org/apache/commons/lang3/SystemUtils.java | 1443 ++++ .../apache/commons/lang3/SystemUtilsTest.java | 364 + .../org/apache/commons/lang3/Validate.java | 1080 +++ .../apache/commons/lang3/ValidateTest.java | 899 +++ .../apache/commons/lang3/builder/Builder.java | 89 + .../lang3/builder/CompareToBuilder.java | 1021 +++ .../lang3/builder/CompareToBuilderTest.java | 1176 +++ .../builder/DefaultToStringStyleTest.java | 133 + .../commons/lang3/builder/EqualsBuilder.java | 945 +++ .../lang3/builder/EqualsBuilderTest.java | 1110 +++ .../lang3/builder/HashCodeBuilder.java | 962 +++ .../HashCodeBuilderAndEqualsBuilderTest.java | 140 + .../lang3/builder/HashCodeBuilderTest.java | 547 ++ .../apache/commons/lang3/builder/IDKey.java | 74 + .../builder/MultiLineToStringStyleTest.java | 132 + .../NoFieldNamesToStringStyleTest.java | 131 + .../builder/ReflectionToStringBuilder.java | 691 ++ ...lectionToStringBuilderConcurrencyTest.java | 120 + .../ReflectionToStringBuilderExcludeTest.java | 137 + ...ngBuilderMutateInspectConcurrencyTest.java | 109 + .../builder/ShortPrefixToStringStyleTest.java | 131 + .../builder/SimpleToStringStyleTest.java | 129 + .../lang3/builder/StandardToStringStyle.java | 560 ++ .../builder/StandardToStringStyleTest.java | 146 + .../lang3/builder/ToStringBuilder.java | 1080 +++ .../lang3/builder/ToStringBuilderTest.java | 1086 +++ .../commons/lang3/builder/ToStringStyle.java | 2280 ++++++ .../builder/ToStringStyleConcurrencyTest.java | 110 + .../lang3/builder/ToStringStyleTest.java | 138 + .../apache/commons/lang3/builder/package.html | 28 + .../AbstractConcurrentInitializerTest.java | 115 + .../lang3/concurrent/AtomicInitializer.java | 107 + .../concurrent/AtomicInitializerTest.java | 39 + .../concurrent/AtomicSafeInitializer.java | 97 + .../concurrent/AtomicSafeInitializerTest.java | 78 + .../concurrent/BackgroundInitializer.java | 335 + .../concurrent/BackgroundInitializerTest.java | 294 + .../lang3/concurrent/BasicThreadFactory.java | 383 + .../concurrent/BasicThreadFactoryTest.java | 302 + .../CallableBackgroundInitializer.java | 126 + .../CallableBackgroundInitializerTest.java | 100 + .../lang3/concurrent/ConcurrentException.java | 70 + .../concurrent/ConcurrentInitializer.java | 54 + .../ConcurrentRuntimeException.java | 72 + .../lang3/concurrent/ConcurrentUtils.java | 391 + .../lang3/concurrent/ConcurrentUtilsTest.java | 548 ++ .../lang3/concurrent/ConstantInitializer.java | 129 + .../concurrent/ConstantInitializerTest.java | 133 + .../lang3/concurrent/LazyInitializer.java | 120 + .../lang3/concurrent/LazyInitializerTest.java | 58 + .../MultiBackgroundInitializer.java | 352 + .../MultiBackgroundInitializerTest.java | 381 + .../lang3/concurrent/TimedSemaphore.java | 422 ++ .../lang3/concurrent/TimedSemaphoreTest.java | 480 ++ .../commons/lang3/concurrent/package.html | 23 + .../lang3/event/EventListenerSupport.java | 315 + .../lang3/event/EventListenerSupportTest.java | 243 + .../commons/lang3/event/EventUtils.java | 131 + .../commons/lang3/event/EventUtilsTest.java | 267 + .../apache/commons/lang3/event/package.html | 22 + .../AbstractExceptionContextTest.java | 176 + .../lang3/exception/CloneFailedException.java | 62 + .../lang3/exception/ContextedException.java | 253 + .../exception/ContextedExceptionTest.java | 98 + .../exception/ContextedRuntimeException.java | 254 + .../ContextedRuntimeExceptionTest.java | 98 + .../exception/DefaultExceptionContext.java | 165 + .../DefaultExceptionContextTest.java | 36 + .../lang3/exception/ExceptionContext.java | 103 + .../lang3/exception/ExceptionUtils.java | 697 ++ .../lang3/exception/ExceptionUtilsTest.java | 508 ++ .../commons/lang3/exception/package.html | 27 + .../apache/commons/lang3/math/Fraction.java | 960 +++ .../commons/lang3/math/FractionTest.java | 1346 ++++ .../commons/lang3/math/IEEE754rUtils.java | 265 + .../commons/lang3/math/IEEE754rUtilsTest.java | 100 + .../commons/lang3/math/NumberUtils.java | 1414 ++++ .../commons/lang3/math/NumberUtilsTest.java | 1279 ++++ .../apache/commons/lang3/math/package.html | 25 + .../apache/commons/lang3/mutable/Mutable.java | 54 + .../commons/lang3/mutable/MutableBoolean.java | 196 + .../lang3/mutable/MutableBooleanTest.java | 131 + .../commons/lang3/mutable/MutableByte.java | 286 + .../lang3/mutable/MutableByteTest.java | 178 + .../commons/lang3/mutable/MutableDouble.java | 315 + .../lang3/mutable/MutableDoubleTest.java | 189 + .../commons/lang3/mutable/MutableFloat.java | 316 + .../lang3/mutable/MutableFloatTest.java | 189 + .../commons/lang3/mutable/MutableInt.java | 276 + .../commons/lang3/mutable/MutableIntTest.java | 188 + .../commons/lang3/mutable/MutableLong.java | 276 + .../lang3/mutable/MutableLongTest.java | 182 + .../commons/lang3/mutable/MutableObject.java | 128 + .../lang3/mutable/MutableObjectTest.java | 95 + .../commons/lang3/mutable/MutableShort.java | 286 + .../lang3/mutable/MutableShortTest.java | 178 + .../apache/commons/lang3/mutable/package.html | 29 + .../org/apache/commons/lang3/overview.html | 23 + .../org/apache/commons/lang3/package.html | 25 + .../lang3/reflect/ConstructorUtils.java | 289 + .../lang3/reflect/ConstructorUtilsTest.java | 224 + .../commons/lang3/reflect/FieldUtils.java | 595 ++ .../commons/lang3/reflect/FieldUtilsTest.java | 1143 +++ .../commons/lang3/reflect/MemberUtils.java | 185 + .../commons/lang3/reflect/MethodUtils.java | 537 ++ .../lang3/reflect/MethodUtilsTest.java | 410 ++ .../commons/lang3/reflect/TypeUtils.java | 1101 +++ .../commons/lang3/reflect/TypeUtilsTest.java | 724 ++ .../apache/commons/lang3/reflect/package.html | 29 + .../commons/lang3/reflect/testbed/Ambig.java | 5 + .../commons/lang3/reflect/testbed/Foo.java | 7 + .../lang3/reflect/testbed/GenericParent.java | 5 + .../reflect/testbed/GenericTypeHolder.java | 5 + .../testbed/PrivatelyShadowedChild.java | 5 + .../lang3/reflect/testbed/PublicChild.java | 5 + .../testbed/PubliclyShadowedChild.java | 5 + .../reflect/testbed/StaticContainer.java | 27 + .../reflect/testbed/StaticContainerChild.java | 5 + .../testbed/StringParameterizedChild.java | 5 + .../lang3/reflect/testbed/test/Ambig.java | 23 + .../lang3/reflect/testbed/test/Bar.java | 24 + .../lang3/reflect/testbed/test/Foo.java | 24 + .../reflect/testbed/test/GenericParent.java | 25 + .../testbed/test/GenericTypeHolder.java | 30 + .../lang3/reflect/testbed/test/Parent.java | 28 + .../testbed/test/PrivatelyShadowedChild.java | 28 + .../reflect/testbed/test/PublicChild.java | 25 + .../testbed/test/PubliclyShadowedChild.java | 28 + .../reflect/testbed/test/StaticContainer.java | 52 + .../testbed/test/StaticContainerChild.java | 24 + .../test/StringParameterizedChild.java | 25 + .../commons/lang3/text/CompositeFormat.java | 116 + .../lang3/text/CompositeFormatTest.java | 90 + .../lang3/text/ExtendedMessageFormat.java | 535 ++ .../lang3/text/ExtendedMessageFormatTest.java | 454 ++ .../commons/lang3/text/FormatFactory.java | 42 + .../commons/lang3/text/FormattableUtils.java | 151 + .../lang3/text/FormattableUtilsTest.java | 117 + .../apache/commons/lang3/text/StrBuilder.java | 2856 ++++++++ .../text/StrBuilderAppendInsertTest.java | 1370 ++++ .../commons/lang3/text/StrBuilderTest.java | 1775 +++++ .../apache/commons/lang3/text/StrLookup.java | 172 + .../commons/lang3/text/StrLookupTest.java | 69 + .../apache/commons/lang3/text/StrMatcher.java | 436 ++ .../commons/lang3/text/StrMatcherTest.java | 207 + .../commons/lang3/text/StrSubstitutor.java | 926 +++ .../lang3/text/StrSubstitutorTest.java | 570 ++ .../commons/lang3/text/StrTokenizer.java | 1114 +++ .../commons/lang3/text/StrTokenizerTest.java | 833 +++ .../apache/commons/lang3/text/WordUtils.java | 497 ++ .../commons/lang3/text/WordUtilsTest.java | 355 + .../apache/commons/lang3/text/package.html | 26 + .../text/translate/AggregateTranslator.java | 60 + .../translate/CharSequenceTranslator.java | 125 + .../text/translate/CodePointTranslator.java | 56 + .../lang3/text/translate/EntityArrays.java | 425 ++ .../text/translate/EntityArraysTest.java | 68 + .../text/translate/LookupTranslator.java | 80 + .../text/translate/LookupTranslatorTest.java | 39 + .../text/translate/NumericEntityEscaper.java | 119 + .../translate/NumericEntityEscaperTest.java | 63 + .../translate/NumericEntityUnescaper.java | 139 + .../translate/NumericEntityUnescaperTest.java | 75 + .../lang3/text/translate/OctalUnescaper.java | 60 + .../text/translate/OctalUnescaperTest.java | 56 + .../lang3/text/translate/UnicodeEscaper.java | 130 + .../text/translate/UnicodeEscaperTest.java | 51 + .../text/translate/UnicodeUnescaper.java | 66 + .../text/translate/UnicodeUnescaperTest.java | 55 + .../commons/lang3/text/translate/package.html | 27 + .../commons/lang3/time/DateFormatUtils.java | 323 + .../lang3/time/DateFormatUtilsTest.java | 247 + .../apache/commons/lang3/time/DateParser.java | 99 + .../commons/lang3/time/DatePrinter.java | 125 + .../apache/commons/lang3/time/DateUtils.java | 1834 +++++ .../lang3/time/DateUtilsFragmentTest.java | 497 ++ .../lang3/time/DateUtilsRoundingTest.java | 703 ++ .../commons/lang3/time/DateUtilsTest.java | 1595 ++++ .../lang3/time/DurationFormatUtils.java | 662 ++ .../lang3/time/DurationFormatUtilsTest.java | 566 ++ .../commons/lang3/time/FastDateFormat.java | 590 ++ .../lang3/time/FastDateFormatTest.java | 312 + .../lang3/time/FastDateFormat_ParserTest.java | 33 + .../time/FastDateFormat_PrinterTest.java | 33 + .../commons/lang3/time/FastDateParser.java | 832 +++ .../lang3/time/FastDateParserTest.java | 360 + .../commons/lang3/time/FastDatePrinter.java | 1243 ++++ .../lang3/time/FastDatePrinterTest.java | 263 + .../commons/lang3/time/FormatCache.java | 212 + .../apache/commons/lang3/time/StopWatch.java | 382 + .../commons/lang3/time/StopWatchTest.java | 220 + .../apache/commons/lang3/time/package.html | 23 + .../commons/lang3/tuple/ImmutablePair.java | 104 + .../lang3/tuple/ImmutablePairTest.java | 101 + .../commons/lang3/tuple/ImmutableTriple.java | 104 + .../lang3/tuple/ImmutableTripleTest.java | 113 + .../commons/lang3/tuple/MutablePair.java | 124 + .../commons/lang3/tuple/MutablePairTest.java | 109 + .../commons/lang3/tuple/MutableTriple.java | 133 + .../lang3/tuple/MutableTripleTest.java | 121 + .../org/apache/commons/lang3/tuple/Pair.java | 179 + .../apache/commons/lang3/tuple/PairTest.java | 121 + .../apache/commons/lang3/tuple/Triple.java | 160 + .../commons/lang3/tuple/TripleTest.java | 127 + .../apache/commons/lang3/tuple/package.html | 22 + .../resources/lang2-lang3-clirr-report.html | 265 + ApacheCommonsLang/site/site.xml | 46 + ApacheCommonsLang/site/xdoc/article2_4.xml | 195 + ApacheCommonsLang/site/xdoc/article2_5.xml | 135 + ApacheCommonsLang/site/xdoc/article3_0.xml | 200 + ApacheCommonsLang/site/xdoc/building.xml | 73 + .../site/xdoc/developerguide.xml | 155 + ApacheCommonsLang/site/xdoc/download_lang.xml | 168 + ApacheCommonsLang/site/xdoc/index.xml | 101 + .../site/xdoc/issue-tracking.xml | 102 + ApacheCommonsLang/site/xdoc/mail-lists.xml | 202 + ApacheCommonsLang/site/xdoc/proposal.xml | 97 + .../site/xdoc/release-history.xml | 48 + ApacheCommonsLang/site/xdoc/upgradeto2_0.xml | 688 ++ ApacheCommonsLang/site/xdoc/upgradeto2_1.xml | 163 + ApacheCommonsLang/site/xdoc/upgradeto2_2.xml | 110 + ApacheCommonsLang/site/xdoc/upgradeto2_3.xml | 118 + ApacheCommonsLang/site/xdoc/upgradeto2_4.xml | 153 + ApacheCommonsLang/site/xdoc/upgradeto2_5.xml | 105 + ApacheCommonsLang/site/xdoc/upgradeto2_6.xml | 87 + ApacheCommonsLang/site/xdoc/upgradeto3_0.xml | 172 + ApacheCommonsLang/site/xdoc/userguide.xml | 701 ++ .../test/resources/lang-708-input.txt | 1 + ApacheCommonsLang/test3class.cld | 849 +++ 287 files changed, 109792 insertions(+) create mode 100644 ApacheCommonsLang/.classpath create mode 100644 ApacheCommonsLang/.project create mode 100644 ApacheCommonsLang/.settings/org.eclipse.jdt.core.prefs create mode 100644 ApacheCommonsLang/assembly/bin.xml create mode 100644 ApacheCommonsLang/assembly/src.xml create mode 100644 ApacheCommonsLang/changes/changes.xml create mode 100644 ApacheCommonsLang/changes/release-notes.vm create mode 100644 ApacheCommonsLang/main/resources/templates/release-notes.vm create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/AnnotationUtils.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/AnnotationUtilsTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/ArrayUtils.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/ArrayUtilsAddTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/ArrayUtilsRemoveMultipleTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/ArrayUtilsRemoveTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/ArrayUtilsTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/BitField.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/BitFieldTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/BooleanUtils.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/BooleanUtilsTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/CharEncoding.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/CharEncodingTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/CharRange.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/CharRangeTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/CharSequenceUtils.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/CharSequenceUtilsTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/CharSet.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/CharSetTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/CharSetUtils.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/CharSetUtilsTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/CharUtils.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/CharUtilsPerfRun.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/CharUtilsTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/ClassUtils.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/ClassUtilsTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/EnumUtils.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/EnumUtilsTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/JavaVersion.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/JavaVersionTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/LocaleUtils.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/LocaleUtilsTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/ObjectUtils.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/ObjectUtilsTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/RandomStringUtils.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/RandomStringUtilsTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/Range.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/RangeTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/SerializationException.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/SerializationUtils.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/SerializationUtilsTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/StringEscapeUtils.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/StringEscapeUtilsTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/StringUtils.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/StringUtilsEqualsIndexOfTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/StringUtilsIsTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/StringUtilsStartsEndsWithTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/StringUtilsSubstringTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/StringUtilsTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/StringUtilsTrimEmptyTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/SystemUtils.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/SystemUtilsTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/Validate.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/ValidateTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/builder/Builder.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/builder/CompareToBuilder.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/builder/CompareToBuilderTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/builder/DefaultToStringStyleTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/builder/EqualsBuilder.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/builder/EqualsBuilderTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/builder/HashCodeBuilder.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/builder/HashCodeBuilderAndEqualsBuilderTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/builder/HashCodeBuilderTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/builder/IDKey.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/builder/MultiLineToStringStyleTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/builder/NoFieldNamesToStringStyleTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/builder/ReflectionToStringBuilder.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/builder/ReflectionToStringBuilderConcurrencyTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/builder/ReflectionToStringBuilderExcludeTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/builder/ReflectionToStringBuilderMutateInspectConcurrencyTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/builder/ShortPrefixToStringStyleTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/builder/SimpleToStringStyleTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/builder/StandardToStringStyle.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/builder/StandardToStringStyleTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/builder/ToStringBuilder.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/builder/ToStringBuilderTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/builder/ToStringStyle.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/builder/ToStringStyleConcurrencyTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/builder/ToStringStyleTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/builder/package.html create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/concurrent/AbstractConcurrentInitializerTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/concurrent/AtomicInitializer.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/concurrent/AtomicInitializerTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/concurrent/AtomicSafeInitializer.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/concurrent/AtomicSafeInitializerTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/concurrent/BackgroundInitializer.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/concurrent/BackgroundInitializerTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/concurrent/BasicThreadFactory.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/concurrent/BasicThreadFactoryTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/concurrent/CallableBackgroundInitializer.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/concurrent/CallableBackgroundInitializerTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/concurrent/ConcurrentException.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/concurrent/ConcurrentInitializer.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/concurrent/ConcurrentRuntimeException.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/concurrent/ConcurrentUtils.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/concurrent/ConcurrentUtilsTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/concurrent/ConstantInitializer.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/concurrent/ConstantInitializerTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/concurrent/LazyInitializer.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/concurrent/LazyInitializerTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/concurrent/MultiBackgroundInitializer.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/concurrent/MultiBackgroundInitializerTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/concurrent/TimedSemaphore.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/concurrent/TimedSemaphoreTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/concurrent/package.html create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/event/EventListenerSupport.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/event/EventListenerSupportTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/event/EventUtils.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/event/EventUtilsTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/event/package.html create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/exception/AbstractExceptionContextTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/exception/CloneFailedException.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/exception/ContextedException.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/exception/ContextedExceptionTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/exception/ContextedRuntimeException.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/exception/ContextedRuntimeExceptionTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/exception/DefaultExceptionContext.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/exception/DefaultExceptionContextTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/exception/ExceptionContext.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/exception/ExceptionUtils.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/exception/ExceptionUtilsTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/exception/package.html create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/math/Fraction.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/math/FractionTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/math/IEEE754rUtils.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/math/IEEE754rUtilsTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/math/NumberUtils.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/math/NumberUtilsTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/math/package.html create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/mutable/Mutable.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableBoolean.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableBooleanTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableByte.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableByteTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableDouble.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableDoubleTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableFloat.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableFloatTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableInt.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableIntTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableLong.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableLongTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableObject.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableObjectTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableShort.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableShortTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/mutable/package.html create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/overview.html create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/package.html create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/reflect/ConstructorUtils.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/reflect/ConstructorUtilsTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/reflect/FieldUtils.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/reflect/FieldUtilsTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/reflect/MemberUtils.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/reflect/MethodUtils.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/reflect/MethodUtilsTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/reflect/TypeUtils.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/reflect/TypeUtilsTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/reflect/package.html create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/Ambig.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/Foo.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/GenericParent.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/GenericTypeHolder.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/PrivatelyShadowedChild.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/PublicChild.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/PubliclyShadowedChild.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/StaticContainer.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/StaticContainerChild.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/StringParameterizedChild.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/test/Ambig.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/test/Bar.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/test/Foo.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/test/GenericParent.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/test/GenericTypeHolder.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/test/Parent.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/test/PrivatelyShadowedChild.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/test/PublicChild.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/test/PubliclyShadowedChild.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/test/StaticContainer.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/test/StaticContainerChild.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/test/StringParameterizedChild.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/text/CompositeFormat.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/text/CompositeFormatTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/text/ExtendedMessageFormat.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/text/ExtendedMessageFormatTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/text/FormatFactory.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/text/FormattableUtils.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/text/FormattableUtilsTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/text/StrBuilder.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/text/StrBuilderAppendInsertTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/text/StrBuilderTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/text/StrLookup.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/text/StrLookupTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/text/StrMatcher.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/text/StrMatcherTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/text/StrSubstitutor.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/text/StrSubstitutorTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/text/StrTokenizer.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/text/StrTokenizerTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/text/WordUtils.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/text/WordUtilsTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/text/package.html create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/text/translate/AggregateTranslator.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/text/translate/CharSequenceTranslator.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/text/translate/CodePointTranslator.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/text/translate/EntityArrays.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/text/translate/EntityArraysTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/text/translate/LookupTranslator.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/text/translate/LookupTranslatorTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/text/translate/NumericEntityEscaper.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/text/translate/NumericEntityEscaperTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/text/translate/NumericEntityUnescaper.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/text/translate/NumericEntityUnescaperTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/text/translate/OctalUnescaper.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/text/translate/OctalUnescaperTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/text/translate/UnicodeEscaper.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/text/translate/UnicodeEscaperTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/text/translate/UnicodeUnescaper.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/text/translate/UnicodeUnescaperTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/text/translate/package.html create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/time/DateFormatUtils.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/time/DateFormatUtilsTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/time/DateParser.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/time/DatePrinter.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/time/DateUtils.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/time/DateUtilsFragmentTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/time/DateUtilsRoundingTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/time/DateUtilsTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/time/DurationFormatUtils.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/time/DurationFormatUtilsTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/time/FastDateFormat.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/time/FastDateFormatTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/time/FastDateFormat_ParserTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/time/FastDateFormat_PrinterTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/time/FastDateParser.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/time/FastDateParserTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/time/FastDatePrinter.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/time/FastDatePrinterTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/time/FormatCache.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/time/StopWatch.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/time/StopWatchTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/time/package.html create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/tuple/ImmutablePair.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/tuple/ImmutablePairTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/tuple/ImmutableTriple.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/tuple/ImmutableTripleTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/tuple/MutablePair.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/tuple/MutablePairTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/tuple/MutableTriple.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/tuple/MutableTripleTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/tuple/Pair.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/tuple/PairTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/tuple/Triple.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/tuple/TripleTest.java create mode 100644 ApacheCommonsLang/org/apache/commons/lang3/tuple/package.html create mode 100644 ApacheCommonsLang/site/resources/lang2-lang3-clirr-report.html create mode 100644 ApacheCommonsLang/site/site.xml create mode 100644 ApacheCommonsLang/site/xdoc/article2_4.xml create mode 100644 ApacheCommonsLang/site/xdoc/article2_5.xml create mode 100644 ApacheCommonsLang/site/xdoc/article3_0.xml create mode 100644 ApacheCommonsLang/site/xdoc/building.xml create mode 100644 ApacheCommonsLang/site/xdoc/developerguide.xml create mode 100644 ApacheCommonsLang/site/xdoc/download_lang.xml create mode 100644 ApacheCommonsLang/site/xdoc/index.xml create mode 100644 ApacheCommonsLang/site/xdoc/issue-tracking.xml create mode 100644 ApacheCommonsLang/site/xdoc/mail-lists.xml create mode 100644 ApacheCommonsLang/site/xdoc/proposal.xml create mode 100644 ApacheCommonsLang/site/xdoc/release-history.xml create mode 100644 ApacheCommonsLang/site/xdoc/upgradeto2_0.xml create mode 100644 ApacheCommonsLang/site/xdoc/upgradeto2_1.xml create mode 100644 ApacheCommonsLang/site/xdoc/upgradeto2_2.xml create mode 100644 ApacheCommonsLang/site/xdoc/upgradeto2_3.xml create mode 100644 ApacheCommonsLang/site/xdoc/upgradeto2_4.xml create mode 100644 ApacheCommonsLang/site/xdoc/upgradeto2_5.xml create mode 100644 ApacheCommonsLang/site/xdoc/upgradeto2_6.xml create mode 100644 ApacheCommonsLang/site/xdoc/upgradeto3_0.xml create mode 100644 ApacheCommonsLang/site/xdoc/userguide.xml create mode 100644 ApacheCommonsLang/test/resources/lang-708-input.txt create mode 100644 ApacheCommonsLang/test3class.cld diff --git a/ApacheCommonsLang/.classpath b/ApacheCommonsLang/.classpath new file mode 100644 index 0000000..18f702b --- /dev/null +++ b/ApacheCommonsLang/.classpath @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/ApacheCommonsLang/.project b/ApacheCommonsLang/.project new file mode 100644 index 0000000..6c3ba1a --- /dev/null +++ b/ApacheCommonsLang/.project @@ -0,0 +1,17 @@ + + + ApacheCommonsLang + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/ApacheCommonsLang/.settings/org.eclipse.jdt.core.prefs b/ApacheCommonsLang/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..838bd9d --- /dev/null +++ b/ApacheCommonsLang/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,11 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.7 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.7 diff --git a/ApacheCommonsLang/assembly/bin.xml b/ApacheCommonsLang/assembly/bin.xml new file mode 100644 index 0000000..4c6a171 --- /dev/null +++ b/ApacheCommonsLang/assembly/bin.xml @@ -0,0 +1,44 @@ + + + bin + + tar.gz + zip + + false + + + + LICENSE.txt + NOTICE.txt + RELEASE-NOTES.txt + + + + target + + + *.jar + + + + target/site/apidocs + apidocs + + + diff --git a/ApacheCommonsLang/assembly/src.xml b/ApacheCommonsLang/assembly/src.xml new file mode 100644 index 0000000..60dc662 --- /dev/null +++ b/ApacheCommonsLang/assembly/src.xml @@ -0,0 +1,43 @@ + + + src + + tar.gz + zip + + ${project.artifactId}-${commons.release.version}-src + + + + checkstyle.xml + findbugs-exclude-filter.xml + LICENSE.txt + NOTICE.txt + pom.xml + PROPOSAL.html + RELEASE-NOTES.txt + build.xml + default.properties + build.properties.sample + + + + src + + + diff --git a/ApacheCommonsLang/changes/changes.xml b/ApacheCommonsLang/changes/changes.xml new file mode 100644 index 0000000..136d585 --- /dev/null +++ b/ApacheCommonsLang/changes/changes.xml @@ -0,0 +1,587 @@ + + + + + Commons Lang Changes + + + + + RandomStringUtils throws confusing IAE when end <= start + RandomStringUtils.random(count, 0, 0, false, false, universe, random) always throws java.lang.ArrayIndexOutOfBoundsException + LocaleUtils - unnecessary recursive call in SyncAvoid class. + Javadoc bug in DateUtils#ceiling for Calendar and Object versions. + Use generics in SerializationUtils + SerializationUtils throws ClassNotFoundException when cloning primitive classes + StringUtils equals() relies on undefined behavior + Documentation bug: StringUtils.split + TypeUtilsTest contains incorrect type assignability assertion + TypeUtils.getTypeArguments() misses type arguments for partially-assigned classes + ImmutablePair doc contains nonsense text + ClassUtils.PACKAGE_SEPARATOR Javadoc contains garbage text + EventListenerSupport.ProxyInvocationHandler no longer defines serialVersionUID + StrBuilder is now serializable + Fix Javadoc Ant warnings + Javadoc bug in static inner class DateIterator + Add Triple class (ternary version of Pair) + FastDateFormat supports parse methods + + + + Add API StringUtils.toString(byte[] intput, String charsetName) + Add an example with whitespace in StringUtils.defaultIfEmpty + Add APIs ClassUtils.isPrimitiveWrapper(Class<?>) and isPrimitiveOrWrapper(Class<?>) + Fix createLong() so it behaves like createInteger() + Include the actual type in the Validate.isInstance and isAssignableFrom exception messages + Incorrect Bundle-SymbolicName in Manifest + Deprecating chomp(String, String) + NumberUtils does not handle upper-case hex: 0X and -0X + StringUtils throws java.security.AccessControlException on Google App Engine + Ant build has wrong component.name + CharUtils static final array CHAR_STRING is not needed to compute CHAR_STRING_ARRAY + Document that the Mutable numbers don't work as expected with String.format + SystemUtils.IS_OS_UNIX doesn't recognize FreeBSD as a Unix system + + + + SerializationUtils.clone: Fallback to context classloader if class not found in current classloader. + ToStringBuilderTest.testReflectionHierarchyArrayList fails with IBM JDK 6. + StringEscapeUtils.escapeXml(input) wrong when input contains characters in Supplementary Planes. + StringEscapeUtils.escapeEcmaScript from lang3 cuts off long unicode string. + Improve exception message when StringUtils.replaceEachRepeatedly detects recursion. + Specify source encoding for Ant build. + Complement ArrayUtils.addAll() variants with by-index and by-value removal methods. + Add Range<T> Range<T>.intersectionWith(Range<T>). + Add mode and median Comparable... methods to ObjectUtils. + Add BooleanUtils.and + or varargs methods. + EnumSet -> bit vector. + The CHAR_ARRAY cache in CharUtils duplicates the cache in java.lang.Character. + Deprecate CharUtils.toCharacterObject(char) in favor of java.lang.Character.valueOf(char). + Missing method getRawMessage for ContextedException and ContextedRuntimeException. + Use internal Java's Number caches instead creating new objects. + + + + StringEscapeUtils.escapeXml(input) outputs wrong results when an input contains characters in Supplementary Planes. + build.xml Java 1.5+ updates. + swapCase and *capitalize speedups. + CharSetUtils.squeeze() speedup. + StringUtils doc/comment spelling fixes. + Increase test coverage of FieldUtils read methods and tweak Javadoc. + Add includeantruntime=false to javac targets to quell warnings in ant 1.8.1 and better (and modest performance gain). + StringIndexOutOfBoundsException when calling unescapeHtml4("&#03"). + StringEscapeUtils.escapeEcmaScript from lang3 cuts off long Unicode string. + StringUtils.join throws NPE when toString returns null for one of objects in collection. + Add FormattableUtils class. + Add ClassUtils.getSimpleName() methods. + Add hashCodeMulti varargs method. + Removed DateUtils.UTC_TIME_ZONE. + Convert more of the StringUtils API to take CharSequence. + EqualsBuilder synchronizes on HashCodeBuilder. + StringUtils.isAlpha, isAlphanumeric and isNumeric now return false for "". + Add support for ConcurrentMap.putIfAbsent(). + Documented potential NPE if auto-boxing occurs for some BooleanUtils methods. + DateUtils.isSameLocalTime compares using 12 hour clock and not 24 hour. + Extend exception handling in ConcurrentUtils to runtime exceptions. + SystemUtils.getJavaVersionAsFloat throws StringIndexOutOfBoundsException on Android runtime/Dalvik VM. + WordUtils.abbreviate() removed. + Doc bug in DateUtils#ceiling. + StringEscapeUtils.unescapeJava doesn't handle octal escapes and Unicode with extra u. + org.apache.commons.lang3.math.Fraction does not reduce (Integer.MIN_VALUE, 2^k). + org.apache.commons.lang3.math.Fraction does not always succeed in multiplyBy and divideBy. + Change ObjectUtils min() & max() functions to use varargs rather than just two parameters. + Add a Null-safe compare() method to ObjectUtils. + NumberUtils.isNumber(String) is not right when the String is "1.1L". + EntityArrays typo: {"\u2122", "&minus;"}, // minus sign, U+2212 ISOtech. + Some Entitys like &Ouml; are not matched properly against its ISO8859-1 representation. + Example StringUtils.indexOfAnyBut("zzabyycdxx", '') = 0 incorrect. + Add StringUtils.defaultIfBlank(). + Provide a very basic ConcurrentInitializer implementation. + Support lazy initialization using atomic variables. + Enhance StrSubstitutor to support nested ${var-${subvr}} expansion. + Provide documentation about the new concurrent package. + Charset may not be threadsafe, because the HashSet is not synch. + StringEscapeUtils.escapeXML() can't process UTF-16 supplementary characters. + StringUtils.endsWithAny method. + Add AnnotationUtils. + BooleanUtils.toBooleanObject to support single character input. + FastDateFormat.format() outputs incorrect week of year because locale isn't respected. + StrSubstitutor should also handle the default properties of a java.util.Properties class. + Javadoc StringUtils.left() claims to throw on negative len, but doesn't. + Add normalizeSpace to StringUtils. + NumberUtils createNumber throws a StringIndexOutOfBoundsException when argument containing "e" and "E" is passed in. + + NOTE: The below were included in the Commons Lang 3.0-beta release. + Convert StringUtils API to take CharSequence. + Push down WordUtils to "text" sub-package. + Extend exception handling in ConcurrentUtils to runtime exceptions. + Some StringUtils methods should take an int character instead of char to use String API features. + EqualsBuilder causes StackOverflowException. + DefaultExceptionContext overwrites values in recursive situations. + ContextedRuntimeException no longer an 'unchecked' exception. + Add Builder Interface / Update Builders to Implement It. + Javadoc is incorrect for public static int lastIndexOf(String str, String searchStr). + ClassUtils.getClass(): Allow Dots as Inner Class Separators. + DateUtils equal & compare functions up to most significant field. + Remove JDK 1.2/1.3 bug handling in StringUtils.indexOf(String, String, int). + Create a basic Pair<L, R> class. + exception.DefaultExceptionContext.getFormattedExceptionMessage catches Throwable. + Provide an implementation of the ThreadFactory interface. + Add new Validate methods. + ArrayUtils.add(T[] array, T element) can create unexpected ClassCastException. + Do the test cases really still require main() and suite() methods?. + @SuppressWarnings("unchecked") is used too generally. + Improve StrLookup API documentation. + Change Java package name. + Change Maven groupId. + New TimedSemaphore class. + Added validState validation method. + Added isAssignableFrom and isInstanceOf validation methods. + Add TypeUtils class to provide utility code for working with generic types. + Replace Range classes with generic version. + Use Iterable on API instead of Collection. + Add methods to Validate to check whether the index is valid for the array/list/string. + Add ability to create a Future for a constant. + Replace StringBuffer with StringBuilder. + Make NumericEntityEscaper immutable. + Compile commons.lang for CDC 1.1/Foundation 1.1. + Add ArrayUtils.toArray to create generic arrays. + Validate: support for validating blank strings. + Add a concurrent package. + Mutable classes should implement an appropriately typed Mutable interface. + Better EnumUtils. + StringEscapeUtils.unescapeJava should support \u+ notation. + Rewrite StringEscapeUtils. + bring ArrayUtils.isEmpty to the generics world. + Add support for background initialization. + Add support for the handling of ExecutionExceptions. + Add StringEscapeUtils.escapeText() methods. + Addition of ContextedException and ContextedRuntimeException. + A generic implementation of the Lazy initialization pattern. + Remove code that does not hold enough value to remain. + Remove code handled now by the JDK. + StrSubstitutor now supports substitution in variable names. + Possible race-conditions in hashCode of the range classes. + StringEscapeUtils.escapeHtml incorrectly converts Unicode characters above U+00FFFF into 2 characters. + Document where in SVN trunk is. + StopWatch does not resist to system time changes. + Fixes for thread safety. + Refactor Validate.java to eliminate code redundancy. + Lower Ascii Characters don't get encoded by Entities.java. + StringUtils.emptyToNull. + StringEscapeUtils.escapeHTML() does not escape chars (0x00-0x20). + Remove @deprecateds. + Add ClassUtils.isAssignable() variants with autoboxing. + Improve Javadoc for StringUtils class. + Javadoc incorrect for StringUtils.endsWithIgnoreCase. + Investigate for vararg usages. + JDK 1.5 build/runtime failure on LANG-393 (EqualsBuilder). + LeftOf/RightOfNumber in Range convenience methods necessary. + ExceptionUtils not thread-safe. + ObjectUtils.coalesce. + StrBuilder should implement CharSequence and Appendable. + StringEscapeUtils.escapeHtml() escapes multibyte characters like Chinese, Japanes, etc. + Finally start using generics. + StrBuilder does not implement clone(). + EnumUtils for JDK 5.0. + Wish : method unaccent. + MutableBigDecimal and MutableBigInteger. + StringEscaper.escapeXml() escapes characters > 0x7f. + Depend on JDK 1.5+. + + + + BooleanUtils: use same optimization in toBooleanObject(String) as in toBoolean(String). + ClassUtils: allow Dots as Inner Class Separators in getClass(). + DateUtils: equal and compare functions up to most significant field. + DateUtils: provide a Date to Calendar convenience method. + ObjectUtils: add clone methods to ObjectUtils. + ObjectUtils: add a Null-safe compare() method. + ObjectUtils: add notEqual() method. + StrBuilder: implement clone() method. + StringUtils: add a normalizeSpace() method. + StringUtils: add endsWithAny() method. + StringUtils: add defaultIfBlank() method. + StrSubstitutor: add a replace(String, Properties) variant. + StrSubstitutor: support substitution in variable names. + Use StrBuilder instead of StringBuffer to improve performance where sync. is not an issue. + CharSet: make the underlying set synchronized. + CompareToBuilder: fix passing along compareTransients to the reflectionCompare method. + ExtendedMessageFormat doesn't override equals(Object). + FastDateFormat: fix to properly include the locale when formatting a Date. + NumberUtils: createNumber() throws a StringIndexOutOfBoundsException when argument containing "e" and "E" is passed in. + StringUtils methods do not handle Unicode 2.0+ supplementary characters correctly. + SystemUtils: getJavaVersionAsFloat throws StringIndexOutOfBoundsException on Android runtime/Dalvik VM. + MemberUtils: getMatchingAccessibleMethod does not correctly handle inheritance and method overloading. + Javadoc is incorrect for lastIndexOf() method. + Javadoc for HashCodeBuilder.append(boolean) does not match implementation. + Javadoc StringUtils.left() claims to throw an exception on negative lenth, but doesn't. + Javadoc - document thread safety. + Test for StringUtils replaceChars() icelandic characters. + + + + ArrayUtils - add isNotEmpty() methods. + ArrayUtils - add nullToEmpty() methods. + CharRange - provide an iterator that lets you walk the chars in the range. + CharRange - add more readable static builder methods. + ClassUtils - new isAssignable() methods with autoboxing. + ClassUtils - add support to getShortClassName and getPackageName for arrays. + DateUtils - add ceiling() method. + DateUtils - add parseDateStrictly() method. + EqualsBuilder - add reset() method. + NumberUtils - add toByte() and toShort() methods. + Mutable numbers - add string constructors. + MutableBoolean - add toBoolean(), isTrue() and isFalse() methods. + StrBuilder - add appendSeparator() methods with an alternative default separator if the StrBuilder is currently empty. + SystemUtils - add IS_OS_WINDOWS_7 constant. + SystemUtils - add IS_JAVA_1_7 constant for JDK 1.7. + StringUtils - add abbreviateMiddle() method. + StringUtils - add indexOfIgnoreCase() and lastIndexOfIgnoreCase() methods. + StringUtils - add isAllUpperCase() and isAllLowerCase() methods. + StringUtils - add lastOrdinalIndexOf() method to complement the existing ordinalIndexOf() method. + StringUtils - add repeat() method. + StringUtils - add startsWithAny() method. + StringUtils - add upperCase(String, Locale) and lowerCase(String, Locale) methods. + New Reflection package containing ConstructorUtils, FieldUtils, MemberUtils and MethodUtils. + ArrayUtils - addAll() does not handle mixed types very well. + CharSet - Synchronizing the COMMON Map so that getInstance doesn't miss a put from a subclass in another thread. + ClassUtils - improving performance of getAllInterfaces. + ClassUtils - toClass() throws NullPointerException on null array element. + DateUtils - Fix parseDate() cannot parse ISO8601 dates produced by FastDateFormat. + DateUtils - round() doesn't work correct for Calendar.AM_PM. + DateUtils - improve tests. + Entities - multithreaded initialization. + Entities - missing final modifiers; thread-safety issues. + EnumUtils - getEnum() doesn't work well in 1.5+. + ExceptionUtils - use immutable lock target. + ExtendedMessageFormat - OutOfMemory with a pattern containing single quotes. + FastDateFormat - call getTime() on a calendar to ensure timezone is in the right state. + FastDateFormat - Remove unused field. + LocaleUtils - Initialization of available locales in LocaleUtils can be deferred. + NumberUtils - createNumber() thows a StringIndexOutOfBoundsException when only an "l" is passed in. + NumberUtils - isNumber(String) and createNumber(String) both modified to support '2.'. + StringUtils - improve handling of case-insensitive Strings. + StringUtils - replaceEach() no longer NPEs when null appears in the last String[]. + StringUtils - correct Javadoc for startsWith() and startsWithIgnoreCase(). + StringEscapeUtils - escapeJava() escapes '/' characters. + StringEscapeUtils - change escapeJavaStyleString() to throw UnhandledException instead swallowing IOException and returning null. + WordUtils - fix StringIndexOutOfBoundsException when lower is greater than the String length. + StrBuilder - Performance improvement by doubling the size of the String in ensureCapacity. + Compare, Equals and HashCode builders - use ArrayUtils to avoid creating a temporary List. + EqualsBuilder - removing the special handling of BigDecimal (LANG-393) to use compareTo instead of equals because it creates an inequality with HashCodeBuilder. + HashCodeBuilder - Performance improvement: check for isArray to short-circuit the 9 instanceof checks. + HashCodeBuilder - Changing the hashCode() method to return toHashCode(). + HashCodeBuilder - reflectionHashCode() can generate incorrect hashcodes. + HashCodeBuilder and ToStringStyle - use of ThreadLocal causes memory leaks in container environments. + ToStringBuilder - make default style thread-safe. + RandomUtils - nextLong() always produces even numbers. + RandomUtils - RandomUtils tests are failing frequently. + + + + ClassUtils.getShortClassName(String) inefficient. + Shouldn't Commons Lang's StringUtils have a "common" string method?. + FastDateFormat getDateInstance() and getDateTimeInstance() assume Locale.getDefault() won't change. + OSGi-ify Lang. + StrBuilder appendFixedWidth does not handle nulls. + infinite loop in Fraction.reduce when numerator == 0. + FastDateFormat thread safety. + ClassUtils.getShortClassName and ClassUtils.getPackageName and class of array. + LocaleUtils.toLocale() rejects strings with only language+variant. + Enum is not thread-safe. + BooleanUtils.toBoolean() - invalid drop-thru in case statement causes StringIndexOutOfBoundsException. + ArrayUtils.toClass. + Why does appendIdentityToString return null?. + NumberUtils.min(floatArray) returns wrong value if floatArray[0] happens to be Float.NaN. + Dates.round() behaves incorrectly for minutes and seconds. + StringUtils.length(String) returns null-safe length. + adding a StringUtils.replace method that takes an array or List of replacement strings. + Adding functionality to DateUtils to allow direct setting of various fields. + Add escaping for CSV columns to StringEscapeUtils. + StringUtils: startsWith / endsWith / startsWithIgnoreCase / endsWithIgnoreCase / removeStartIgnoreCase / removeEndIgnoreCase methods. + Extension to ClassUtils: Obtain the primitive class from a wrapper. + Javadoc bugs - cannot find object. + Optimize HashCodeBuilder.append(Object). + http://commons.apache.org/lang/developerguide.html "Building" section is incorrect and incomplete. + Ambiguous / confusing names in StringUtils replace* methods. + Add new splitByWholeSeparatorPreserveAllTokens() methods to StringUtils. + Add getStartTime to StopWatch. + Perhaps add containsAny() methods?. + Javadoc Example for EqualsBuilder is questionable. + EqualsBuilder don't compare BigDecimals correctly. + Split camel case strings. + Add Calendar flavour format methods to DateFormatUtils. + Calculating A date fragment in any time-unit. + Memory usage improvement for StringUtils#getLevenshteinDistance(). + Add ExtendedMessageFormat to org.apache.commons.lang.text. + StringEscapeUtils.escapeJavaScript() method did not escape '/' into '\/', it will make IE render page uncorrectly. + Add toArray() method to IntRange and LongRange classes. + add SystemUtils.IS_OS_WINDOWS_VISTA field. + Pointless synchronized in ThreadLocal.initialValue should be removed. + ToStringStyle Javadoc should show examples of styles. + Documentation bug for ignoreEmptyTokens accessors in StrTokenizer. + BooleanUtils toBooleanObject Javadoc does not match implementation. + truncateNicely method which avoids truncating in the middle of a word. + + + + Use of enum prevents a classloader from being garbage collected resuling in out of memory exceptions. + NumberUtils.max(byte[]) and NumberUtils.min(byte[]) are missing. + Null-safe comparison methods for finding most recent / least recent dates. + StopWatch: suspend() acts as split(), if followed by stop(). + StrBuilder.replaceAll and StrBuilder.deleteAll can throw ArrayIndexOutOfBoundsException. + Bug in method appendFixedWidthPadRight of class StrBuilder causes an ArrayIndexOutOfBoundsException. + ToStringBuilder throws StackOverflowError when an Object cycle exists. + Create more tests to test out the +=31 replacement code in DurationFormatUtils. + StrBuilder contains usages of thisBuf.length when they should use size. + Enum Javadoc: 1) outline 5.0 native Enum migration 2) warn not to use the switch() , 3) point out approaches for persistence and gui. + Wrong behavior of Entities.unescape. + NumberUtils.createNumber throws NumberFormatException for one digit long. + NullPointerException in isAvailableLocale(Locale). + FastDateFormat.mRules is not transient or serializable. + StringUtils.join should allow you to pass a range for it (so it only joins a part of the array). + Refactor Entities methods. + Tests fail to pass when building with Maven 2. + DurationFormatUtils returns wrong result. + unescapeXml("&12345678;") should be "&12345678;". + Optimize StringEscapeUtils.unescapeXml(String). + BooleanUtils isNotTrue/isNotFalse. + Extra StrBuilder methods. + Add a pair of StringUtils.substringsBetween;String[] methods. + HashCodeBuilder throws java.lang.StackOverflowError when an object contains a cycle. + Wish for StringUtils.join(Collection, *). + + + + StrBuilderTest#testReplaceStringString fails. + EqualsBuilder.append(Object[], Object[]) crashes with a NullPointerException if an element of the first array is null. + Serialization - not backwards compatible. + Replace Clover with Cobertura. + ValuedEnum.compareTo(Object other) not typesafe - it easily could be... + LocaleUtils test fails under Mustang. + Javadoc example for StringUtils.splitByWholeSeparator incorrect. + PADDING array in StringUtils overflows on '\uffff'. + ClassUtils.primitiveToWrapper and Void. + unit test for org.apache.commons.lang.text.StrBuilder. + DateUtils.truncate method is buggy when dealing with DST switching hours. + RandomStringUtils.random() family of methods create invalid Unicode sequences. + StringUtils#getLevenshteinDistance() performance is sub-optimal. + Wrong length check in StrTokenizer.StringMatcher. + ExceptionUtils goes into infinite loop in getThrowables is throwable.getCause() == throwable. + FastDateFormat: wrong format for date "01.01.1000". + Unclear Javadoc for DateUtils.iterator(). + Memory "leak" in StringUtils. + StringEscapeUtils should expose escape*() methods taking Writer argument. + Fraction.toProperString() returns -1/1 for -1. + DurationFormatUtils.formatDurationWords "11 <unit>s" gets converted to "11 <unit>". + Performance modifications on StringUtils.replace. + StringEscapeUtils.unescapeHtml skips first entity after standalone ampersand. + DurationFormatUtils.formatPeriod() returns the wrong result. + Request for MutableBoolean implementation. + New method for EqualsBuilder. + New ExceptionUtils method setCause(). + Add Mutable<Type> to<Type>() methods. + Provides a Class.getPublicMethod which returns public invocable Method. + Using ReflectionToStringBuilder and excluding secure fields. + add generic add method to DateUtils. + Tokenizer Enhancements: reset input string, static CSV/TSV factories. + Trivial cleanup of Javadoc in various files. + CompositeFormat. + Performance boost for RandomStringUtils. + Enhanced Class.forName version. + Add StringUtils.containsIgnoreCase(...). + Support char array converters on ArrayUtils. + DurationFormatUtils.formatDurationISO() Javadoc is missing T in duration string between date and time part. + Minor build and checkstyle changes. + Javadoc errors on StringUtils.splitPreserveAllTokens(String, char). + EscapeUtil.escapeHtml() should clarify that it does not escape ' chars to &apos;. + Add methods and tests to StrBuilder. + replace() length calculation improvement. + New interpolation features. + Implementation of escape/unescapeHtml methods with Writer. + CompareToBuilder excludeFields for reflection method. + Add WordUtils.getInitials(String). + Error in an example in the Javadoc of the StringUtils.splitPreserveAllTokens() method. + ToStringBuilder/HashCodeBuilder Javadoc code examples. + Cannot build tests from latest SVN. + minor Javadoc improvements for StringUtils.stripXxx() methods. + Javadoc for StringUtils.removeEnd is incorrect. + Minor tweak to fix of bug # 26616. + + + + make optional parameters in FastDateFormat really optional. + Nestable.indexOfThrowable(Class) uses Class.equals() to match. + buffer under/overrun on Strings.strip, stripStart & stripEnd. + ToStringStyle.setArrayEnd(String) doesn't replace null with empty string. + New class proposal: CharacterEncoding. + SystemUtils fails init on HP-UX. + Javadoc - 'four basic XML entities' should be 5 (apos is missing). + o.a.c.lang.enum.ValuedEnum: 'enum'is a keyword in JDK1.5.0. + StringEscapeUtils.unescapeHtml() doesn't handle an empty entity. + EqualsBuilder.append(Object[], Object[]) incorrectly checks that rhs[i] is instance of lhs[i]'s class. + Method enums.Enum.equals(Object o) doesn't work correctly. + ExceptionUtils.addCauseMethodName(String) does not check for duplicates. + Make StopWatch validate state transitions. + enum package is not compatible with 1.5 jdk. + WordUtils capitalizeFully() throws a null pointer exception. + ValuedEnum. + parseDate class from HttpClient's DateParser class. + ArrayUtils.isEquals() throws ClassCastException when array1 and array2 are different dimension. + ClassCastException in Enum.equals(Object). + FastDateFormat year bug. + unbalanced ReflectionToStringBuilder. + FastDateFormat.getDateInstance(int, Locale) always uses the pattern from the first invocation. + ReflectionToStringBuilder.toString(null) throws exception by design. + Make ClassUtils methods null-safe and not throw an IAE. + StringUtils.split ignores empty items. + EqualsBuilder.append(Object[], Object[]) throws NPE. + ArrayUtils.addAll doesn't always return new array. + Enum.equals does not handle different class loaders. + Add SystemUtils.AWT_TOOLKIT and others. + Throwable cause for NotImplementedException. + ClassUtils.primitivesToWrappers method. + public static boolean DateUtils.equals(Date dt1, Date dt2) ?. + Documentation error in StringUtils.replace. + DateUtils constants should be long. + DateUtils.truncate() is off by one hour when using a date in DST switch 'zone'. + StringEscapeUtils.unescapeHtml() doesn't handle hex entities. + new StringUtils.replaceChars behaves differently from old CharSetUtils.translate. + last substring returned by StringUtils.split( String, String, int ) is too long. + Can't subclass EqualsBuilder because isEquals is private. + new StringUtils.split methods that split on the whole separator string. + New method for converting a primitive Class to its corresponding wrapper Class. + Add convenience format(long) methods to FastDateFormat. + Enum's outer class may not be loaded for EnumUtils. + WordUtils.capitalizeFully(String str) should take a delimiter. + Make Javadoc crosslinking configurable. + Minor Javadoc fixes for StringUtils.contains(String, String). + Error in Javadoc for StringUtils.chomp(String, String). + StringUtils.defaultString: Documentation error. + Add hashCode-support to class ObjectUtils. + add another "known method" to ExceptionUtils. + Enhancement of ExceptionUtils.CAUSE_METHOD_NAMES. + DateUtils.truncate oddity at the far end of the Date spectrum. + add getLength() method to ArrayUtils. + Validate.java: fixes comment skew, removes unused loop counter. + StringUtils.isAsciiPrintable(). + ExceptionUtils: new getCause() methodname (for tomcat-exception). + fixes 75 typos. + mutable numbers. + Javadoc fixes for ClassUtils. + Add StringUtils.nIndexOf?. + Javadoc fixes for CharSetUtils. + Remove redundant check for null separator in StringUtils#join. + Class and Package Comparators for ClassUtils. + add remove methods to ArrayUtils. + WordUtils capitalize improvement. + add isEmpty method to ArrayUtils. + lang.math.Fraction class deficiencies. + Add methods to ArrayUtils: add at end and insert-like ops. + Add SystemUtils methods for directory properties. + Add method that validates Collection elements are a certain type. + elapsed time formatting utility method. + + + + Infinite loop in ToStringBuilder.reflectionToString for inner classes. + NumberUtils.createBigDecimal("") NPE in Sun 1.3.1_08. + Rationalize StringUtils slice functions. + SystemUtils.IS_OS_OS2 Javadoc is wrong. + A small, but important Javadoc fix for Fraction proper whole and numerator. + Adding tolerance to double[] search methods in ArrayUtils. + lang.builder classes Javadoc edits (mostly typo fixes). + StringUtils Javadoc and test enhancements. + SystemUtils.IS_OS_*, IS_JAVA_* are always false. + Improve util.Validate tests. + maven-beta10 checkstyle problem. + StringUtils.chopNewLine - StringIndexOutOfBoundsException. + ToStringBuilder doesn't work well in subclasses. + static option for reversing the stacktrace. + NullPointerException in CompareToBuilder. + RandomStringUtils.randomAlpha methods omit 'z'. + test.time fails in Japanese (non-us) locale. + NumberUtils.isNumber allows illegal trailing characters. + Improve Javadoc and overflow behavior of Fraction. + RandomStringUtils infloops with length > 1. + test.lang fails if compiled with non iso-8859-1 locales. + SystemUtils does not play nice in an Applet. + time unit tests fail on Sundays. + java.lang.ExceptionInInitializerError thrown by JVMRandom constructor. + StringUtils.chomp does not match Perl. + patch and test case fixing problem with RandomStringUtils.random(). + General case: infinite loop: ToStringBuilder.reflectionToString. + Should ToStringBuilder.reflectionToString handle arrays?. + EnumUtils nit: The import java.io.Serializable is never used. + Example in Javadoc for ToStringBuilder wrong for append. + Added class hierachy support to HashCodeBuilder.reflectionHashCode(). + ExceptionUtils new methods. + Infinite loop in StringUtils.replace(text, repl, with) + FIX. + StackOverflow due to ToStringBuilder. + No Javadoc for NestableDelegate. + Specify initial size for Enum's HashMap. + Enum does not support inner sub-classes. + Removed compile warning in ObjectUtils. + SystemUtils.IS_JAVA_1_5 Javadoc is wrong. + NumberRange inaccurate for Long, etc. + Hierarchy support in ToStringBuilder.reflectionToString(). + StringUtils.countMatches loops forever if substring empty. + Javadoc fixes (remove @links to non-public identifiers). + Add Javadoc examples and tests for StringUtils. + Make NumberUtils null handling consistent. + Unused field 'startFinal' in DateIterator. + reduce object creation in ToStringBuilder. + Improved tests, Javadoc for CharSetUtils, StringEscapeUtils. + NumberUtils min/max, BooleanUtils.xor, and ArrayUtils toPrimitive and toObject. + Javadoc, tests improvements for CharSet, CharSetUtils. + StringUtil enhancement. + Javadoc nit. + Additional Lang Method Suggestions. + Make NestableDelegate methods public instead of package private. + Missing @since tags. + Refactored reflection feature of ToStringBuilder into new ReflectionToStringBuilder. + Typo in documentation. + Patch for Javadoc. + Add join(..., char c) to StringUtils (and some performance fixes). Even contains tests!. + Resurrect the WordWrapUtils from commons-sandbox/utils. + EnumTest fails on Linux Sun JDK 1.3.0. + What to do with FastDateFormat unused private constructors. + Added class hierachy support to CompareToBuilder.reflectionCompare(). + Removed compile warning in FastDateFormat. + typo in the Javadoc example code. + MethodUtils: Removed unused code/unused local vars. + Hierarchy support in EqualsBuilder.reflectionEquals(). + Javadoc Errata. + ArrayUtils.contains(). + More flexibility for getRootCause in ExceptionUtils. + + + + NumberRange.getMaximum returns minimum. + Enum constructor validations. + NestableException/Delegate is not serializable. + split using null and max less than actual token count adds "null". + ExceptionUtils cannot handle J2EE-Exception in a default way. + + + + + + + diff --git a/ApacheCommonsLang/changes/release-notes.vm b/ApacheCommonsLang/changes/release-notes.vm new file mode 100644 index 0000000..e6b980f --- /dev/null +++ b/ApacheCommonsLang/changes/release-notes.vm @@ -0,0 +1,138 @@ +## Licensed to the Apache Software Foundation (ASF) under one +## or more contributor license agreements. See the NOTICE file +## distributed with this work for additional information +## regarding copyright ownership. The ASF licenses this file +## to you under the Apache License, Version 2.0 (the +## "License"); you may not use this file except in compliance +## with the License. You may obtain a copy of the License at +## +## http://www.apache.org/licenses/LICENSE-2.0 +## +## Unless required by applicable law or agreed to in writing, +## software distributed under the License is distributed on an +## "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +## KIND, either express or implied. See the License for the +## specific language governing permissions and limitations +## under the License. + Apache ${project.name} ${version} RELEASE NOTES + +$introduction.replaceAll("(?Helper methods for working with {@link Annotation} instances.

+ * + *

This class contains various utility methods that make working with + * annotations simpler.

+ * + *

{@link Annotation} instances are always proxy objects; unfortunately + * dynamic proxies cannot be depended upon to know how to implement certain + * methods in the same manner as would be done by "natural" {@link Annotation}s. + * The methods presented in this class can be used to avoid that possibility. It + * is of course also possible for dynamic proxies to actually delegate their + * e.g. {@link Annotation#equals(Object)}/{@link Annotation#hashCode()}/ + * {@link Annotation#toString()} implementations to {@link AnnotationUtils}.

+ * + *

#ThreadSafe#

+ * + * @since 3.0 + * @version $Id: AnnotationUtils.java 1083850 2011-03-21 15:59:10Z mbenson $ + */ +public class AnnotationUtils { + + /** + * A style that prints annotations as recommended. + */ + private static final ToStringStyle TO_STRING_STYLE = new ToStringStyle() { + /** Serialization version */ + private static final long serialVersionUID = 1L; + + { + setDefaultFullDetail(true); + setArrayContentDetail(true); + setUseClassName(true); + setUseShortClassName(true); + setUseIdentityHashCode(false); + setContentStart("("); + setContentEnd(")"); + setFieldSeparator(", "); + setArrayStart("["); + setArrayEnd("]"); + } + + /** + * {@inheritDoc} + */ + @Override + protected String getShortClassName(java.lang.Class cls) { + Class annotationType = null; + for (Class iface : ClassUtils.getAllInterfaces(cls)) { + if (Annotation.class.isAssignableFrom(iface)) { + @SuppressWarnings("unchecked") + //because we just checked the assignability + Class found = (Class) iface; + annotationType = found; + break; + } + } + return new StringBuilder(annotationType == null ? "" : annotationType.getName()) + .insert(0, '@').toString(); + } + + /** + * {@inheritDoc} + */ + @Override + protected void appendDetail(StringBuffer buffer, String fieldName, Object value) { + if (value instanceof Annotation) { + value = AnnotationUtils.toString((Annotation) value); + } + super.appendDetail(buffer, fieldName, value); + } + + }; + + /** + *

{@code AnnotationUtils} instances should NOT be constructed in + * standard programming. Instead, the class should be used statically.

+ * + *

This constructor is public to permit tools that require a JavaBean + * instance to operate.

+ */ + public AnnotationUtils() { + } + + //----------------------------------------------------------------------- + /** + *

Checks if two annotations are equal using the criteria for equality + * presented in the {@link Annotation#equals(Object)} API docs.

+ * + * @param a1 the first Annotation to compare, {@code null} returns + * {@code false} unless both are {@code null} + * @param a2 the second Annotation to compare, {@code null} returns + * {@code false} unless both are {@code null} + * @return {@code true} if the two annotations are {@code equal} or both + * {@code null} + */ + public static boolean equals(Annotation a1, Annotation a2) { + if (a1 == a2) { + return true; + } + if (a1 == null || a2 == null) { + return false; + } + Class type = a1.annotationType(); + Class type2 = a2.annotationType(); + Validate.notNull(type, "Annotation %s with null annotationType()", a1); + Validate.notNull(type2, "Annotation %s with null annotationType()", a2); + if (!type.equals(type2)) { + return false; + } + try { + for (Method m : type.getDeclaredMethods()) { + if (m.getParameterTypes().length == 0 + && isValidAnnotationMemberType(m.getReturnType())) { + Object v1 = m.invoke(a1); + Object v2 = m.invoke(a2); + if (!memberEquals(m.getReturnType(), v1, v2)) { + return false; + } + } + } + } catch (IllegalAccessException ex) { + return false; + } catch (InvocationTargetException ex) { + return false; + } + return true; + } + + /** + *

Generate a hash code for the given annotation using the algorithm + * presented in the {@link Annotation#hashCode()} API docs.

+ * + * @param a the Annotation for a hash code calculation is desired, not + * {@code null} + * @return the calculated hash code + * @throws RuntimeException if an {@code Exception} is encountered during + * annotation member access + * @throws IllegalStateException if an annotation method invocation returns + * {@code null} + */ + public static int hashCode(Annotation a) { + int result = 0; + Class type = a.annotationType(); + for (Method m : type.getDeclaredMethods()) { + try { + Object value = m.invoke(a); + if (value == null) { + throw new IllegalStateException( + String.format("Annotation method %s returned null", m)); + } + result += hashMember(m.getName(), value); + } catch (RuntimeException ex) { + throw ex; + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + return result; + } + + /** + *

Generate a string representation of an Annotation, as suggested by + * {@link Annotation#toString()}.

+ * + * @param a the annotation of which a string representation is desired + * @return the standard string representation of an annotation, not + * {@code null} + */ + public static String toString(final Annotation a) { + ToStringBuilder builder = new ToStringBuilder(a, TO_STRING_STYLE); + for (Method m : a.annotationType().getDeclaredMethods()) { + if (m.getParameterTypes().length > 0) { + continue; //wtf? + } + try { + builder.append(m.getName(), m.invoke(a)); + } catch (RuntimeException ex) { + throw ex; + } catch (Exception ex) { + throw new RuntimeException(ex); + } + } + return builder.build(); + } + + /** + *

Checks if the specified type is permitted as an annotation member.

+ * + *

The Java language specification only permits certain types to be used + * in annotations. These include {@link String}, {@link Class}, primitive + * types, {@link Annotation}, {@link Enum}, and single-dimensional arrays of + * these types.

+ * + * @param type the type to check, {@code null} + * @return {@code true} if the type is a valid type to use in an annotation + */ + public static boolean isValidAnnotationMemberType(Class type) { + if (type == null) { + return false; + } + if (type.isArray()) { + type = type.getComponentType(); + } + return type.isPrimitive() || type.isEnum() || type.isAnnotation() + || String.class.equals(type) || Class.class.equals(type); + } + + //besides modularity, this has the advantage of autoboxing primitives: + /** + * Helper method for generating a hash code for a member of an annotation. + * + * @param name the name of the member + * @param value the value of the member + * @return a hash code for this member + */ + private static int hashMember(String name, Object value) { + int part1 = name.hashCode() * 127; + if (value.getClass().isArray()) { + return part1 ^ arrayMemberHash(value.getClass().getComponentType(), value); + } + if (value instanceof Annotation) { + return part1 ^ hashCode((Annotation) value); + } + return part1 ^ value.hashCode(); + } + + /** + * Helper method for checking whether two objects of the given type are + * equal. This method is used to compare the parameters of two annotation + * instances. + * + * @param type the type of the objects to be compared + * @param o1 the first object + * @param o2 the second object + * @return a flag whether these objects are equal + */ + private static boolean memberEquals(Class type, Object o1, Object o2) { + if (o1 == o2) { + return true; + } + if (o1 == null || o2 == null) { + return false; + } + if (type.isArray()) { + return arrayMemberEquals(type.getComponentType(), o1, o2); + } + if (type.isAnnotation()) { + return equals((Annotation) o1, (Annotation) o2); + } + return o1.equals(o2); + } + + /** + * Helper method for comparing two objects of an array type. + * + * @param componentType the component type of the array + * @param o1 the first object + * @param o2 the second object + * @return a flag whether these objects are equal + */ + private static boolean arrayMemberEquals(Class componentType, Object o1, Object o2) { + if (componentType.isAnnotation()) { + return annotationArrayMemberEquals((Annotation[]) o1, (Annotation[]) o2); + } + if (componentType.equals(Byte.TYPE)) { + return Arrays.equals((byte[]) o1, (byte[]) o2); + } + if (componentType.equals(Short.TYPE)) { + return Arrays.equals((short[]) o1, (short[]) o2); + } + if (componentType.equals(Integer.TYPE)) { + return Arrays.equals((int[]) o1, (int[]) o2); + } + if (componentType.equals(Character.TYPE)) { + return Arrays.equals((char[]) o1, (char[]) o2); + } + if (componentType.equals(Long.TYPE)) { + return Arrays.equals((long[]) o1, (long[]) o2); + } + if (componentType.equals(Float.TYPE)) { + return Arrays.equals((float[]) o1, (float[]) o2); + } + if (componentType.equals(Double.TYPE)) { + return Arrays.equals((double[]) o1, (double[]) o2); + } + if (componentType.equals(Boolean.TYPE)) { + return Arrays.equals((boolean[]) o1, (boolean[]) o2); + } + return Arrays.equals((Object[]) o1, (Object[]) o2); + } + + /** + * Helper method for comparing two arrays of annotations. + * + * @param a1 the first array + * @param a2 the second array + * @return a flag whether these arrays are equal + */ + private static boolean annotationArrayMemberEquals(Annotation[] a1, Annotation[] a2) { + if (a1.length != a2.length) { + return false; + } + for (int i = 0; i < a1.length; i++) { + if (!equals(a1[i], a2[i])) { + return false; + } + } + return true; + } + + /** + * Helper method for generating a hash code for an array. + * + * @param componentType the component type of the array + * @param o the array + * @return a hash code for the specified array + */ + private static int arrayMemberHash(Class componentType, Object o) { + if (componentType.equals(Byte.TYPE)) { + return Arrays.hashCode((byte[]) o); + } + if (componentType.equals(Short.TYPE)) { + return Arrays.hashCode((short[]) o); + } + if (componentType.equals(Integer.TYPE)) { + return Arrays.hashCode((int[]) o); + } + if (componentType.equals(Character.TYPE)) { + return Arrays.hashCode((char[]) o); + } + if (componentType.equals(Long.TYPE)) { + return Arrays.hashCode((long[]) o); + } + if (componentType.equals(Float.TYPE)) { + return Arrays.hashCode((float[]) o); + } + if (componentType.equals(Double.TYPE)) { + return Arrays.hashCode((double[]) o); + } + if (componentType.equals(Boolean.TYPE)) { + return Arrays.hashCode((boolean[]) o); + } + return Arrays.hashCode((Object[]) o); + } +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/AnnotationUtilsTest.java b/ApacheCommonsLang/org/apache/commons/lang3/AnnotationUtilsTest.java new file mode 100644 index 0000000..25bd92e --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/AnnotationUtilsTest.java @@ -0,0 +1,508 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3; + +import static java.lang.annotation.ElementType.FIELD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import static org.apache.commons.lang3.AnnotationUtilsTest.Stooge.CURLY; +import static org.apache.commons.lang3.AnnotationUtilsTest.Stooge.LARRY; +import static org.apache.commons.lang3.AnnotationUtilsTest.Stooge.MOE; +import static org.apache.commons.lang3.AnnotationUtilsTest.Stooge.SHEMP; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.Target; +import java.lang.reflect.Array; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.Collection; +import java.util.Map; + +import org.junit.Before; +import org.junit.Test; + +/** + * @version $Id: AnnotationUtilsTest.java 1309977 2012-04-05 17:53:39Z ggregory $ + */ +public class AnnotationUtilsTest { + @TestAnnotation( + booleanValue = false, + booleanValues = { false }, + byteValue = 0, + byteValues = { 0 }, + charValue = 0, + charValues = { 0 }, + doubleValue = 0, + doubleValues = { 0 }, + floatValue = 0, + floatValues = { 0 }, + intValue = 0, + intValues = { 0 }, + longValue = 0, + longValues = { 0 }, + nest = @NestAnnotation( + booleanValue = false, + booleanValues = { false }, + byteValue = 0, + byteValues = { 0 }, + charValue = 0, + charValues = { 0 }, + doubleValue = 0, + doubleValues = { 0 }, + floatValue = 0, + floatValues = { 0 }, + intValue = 0, + intValues = { 0 }, + longValue = 0, + longValues = { 0 }, + shortValue = 0, + shortValues = { 0 }, + stooge = CURLY, + stooges = { MOE, LARRY, SHEMP }, + string = "", + strings = { "" }, + type = Object.class, + types = { Object.class } + ), + nests = { + @NestAnnotation( + booleanValue = false, + booleanValues = { false }, + byteValue = 0, + byteValues = { 0 }, + charValue = 0, + charValues = { 0 }, + doubleValue = 0, + doubleValues = { 0 }, + floatValue = 0, + floatValues = { 0 }, + intValue = 0, + intValues = { 0 }, + longValue = 0, + longValues = { 0 }, + shortValue = 0, + shortValues = { 0 }, + stooge = CURLY, + stooges = { MOE, LARRY, SHEMP }, + string = "", + strings = { "" }, + type = Object[].class, + types = { Object[].class } + ) + }, + shortValue = 0, + shortValues = { 0 }, + stooge = SHEMP, + stooges = { MOE, LARRY, CURLY }, + string = "", + strings = { "" }, + type = Object.class, + types = { Object.class } + ) + public Object dummy1; + + @TestAnnotation( + booleanValue = false, + booleanValues = { false }, + byteValue = 0, + byteValues = { 0 }, + charValue = 0, + charValues = { 0 }, + doubleValue = 0, + doubleValues = { 0 }, + floatValue = 0, + floatValues = { 0 }, + intValue = 0, + intValues = { 0 }, + longValue = 0, + longValues = { 0 }, + nest = @NestAnnotation( + booleanValue = false, + booleanValues = { false }, + byteValue = 0, + byteValues = { 0 }, + charValue = 0, + charValues = { 0 }, + doubleValue = 0, + doubleValues = { 0 }, + floatValue = 0, + floatValues = { 0 }, + intValue = 0, + intValues = { 0 }, + longValue = 0, + longValues = { 0 }, + shortValue = 0, + shortValues = { 0 }, + stooge = CURLY, + stooges = { MOE, LARRY, SHEMP }, + string = "", + strings = { "" }, + type = Object.class, + types = { Object.class } + ), + nests = { + @NestAnnotation( + booleanValue = false, + booleanValues = { false }, + byteValue = 0, + byteValues = { 0 }, + charValue = 0, + charValues = { 0 }, + doubleValue = 0, + doubleValues = { 0 }, + floatValue = 0, + floatValues = { 0 }, + intValue = 0, + intValues = { 0 }, + longValue = 0, + longValues = { 0 }, + shortValue = 0, + shortValues = { 0 }, + stooge = CURLY, + stooges = { MOE, LARRY, SHEMP }, + string = "", + strings = { "" }, + type = Object[].class, + types = { Object[].class } + ) + }, + shortValue = 0, + shortValues = { 0 }, + stooge = SHEMP, + stooges = { MOE, LARRY, CURLY }, + string = "", + strings = { "" }, + type = Object.class, + types = { Object.class } + ) + public Object dummy2; + + @TestAnnotation( + booleanValue = false, + booleanValues = { false }, + byteValue = 0, + byteValues = { 0 }, + charValue = 0, + charValues = { 0 }, + doubleValue = 0, + doubleValues = { 0 }, + floatValue = 0, + floatValues = { 0 }, + intValue = 0, + intValues = { 0 }, + longValue = 0, + longValues = { 0 }, + nest = @NestAnnotation( + booleanValue = false, + booleanValues = { false }, + byteValue = 0, + byteValues = { 0 }, + charValue = 0, + charValues = { 0 }, + doubleValue = 0, + doubleValues = { 0 }, + floatValue = 0, + floatValues = { 0 }, + intValue = 0, + intValues = { 0 }, + longValue = 0, + longValues = { 0 }, + shortValue = 0, + shortValues = { 0 }, + stooge = CURLY, + stooges = { MOE, LARRY, SHEMP }, + string = "", + strings = { "" }, + type = Object.class, + types = { Object.class } + ), + nests = { + @NestAnnotation( + booleanValue = false, + booleanValues = { false }, + byteValue = 0, + byteValues = { 0 }, + charValue = 0, + charValues = { 0 }, + doubleValue = 0, + doubleValues = { 0 }, + floatValue = 0, + floatValues = { 0 }, + intValue = 0, + intValues = { 0 }, + longValue = 0, + longValues = { 0 }, + shortValue = 0, + shortValues = { 0 }, + stooge = CURLY, + stooges = { MOE, LARRY, SHEMP }, + string = "", + strings = { "" }, + type = Object[].class, + types = { Object[].class } + ), + //add a second NestAnnotation to break equality: + @NestAnnotation( + booleanValue = false, + booleanValues = { false }, + byteValue = 0, + byteValues = { 0 }, + charValue = 0, + charValues = { 0 }, + doubleValue = 0, + doubleValues = { 0 }, + floatValue = 0, + floatValues = { 0 }, + intValue = 0, + intValues = { 0 }, + longValue = 0, + longValues = { 0 }, + shortValue = 0, + shortValues = { 0 }, + stooge = CURLY, + stooges = { MOE, LARRY, SHEMP }, + string = "", + strings = { "" }, + type = Object[].class, + types = { Object[].class } + ) + }, + shortValue = 0, + shortValues = { 0 }, + stooge = SHEMP, + stooges = { MOE, LARRY, CURLY }, + string = "", + strings = { "" }, + type = Object.class, + types = { Object.class } + ) + public Object dummy3; + + @NestAnnotation( + booleanValue = false, + booleanValues = { false }, + byteValue = 0, + byteValues = { 0 }, + charValue = 0, + charValues = { 0 }, + doubleValue = 0, + doubleValues = { 0 }, + floatValue = 0, + floatValues = { 0 }, + intValue = 0, + intValues = { 0 }, + longValue = 0, + longValues = { 0 }, + shortValue = 0, + shortValues = { 0 }, + stooge = CURLY, + stooges = { MOE, LARRY, SHEMP }, + string = "", + strings = { "" }, + type = Object[].class, + types = { Object[].class } + ) + public Object dummy4; + + @Target(FIELD) + @Retention(RUNTIME) + public @interface TestAnnotation { + String string(); + String[] strings(); + Class type(); + Class[] types(); + byte byteValue(); + byte[] byteValues(); + short shortValue(); + short[] shortValues(); + int intValue(); + int[] intValues(); + char charValue(); + char[] charValues(); + long longValue(); + long[] longValues(); + float floatValue(); + float[] floatValues(); + double doubleValue(); + double[] doubleValues(); + boolean booleanValue(); + boolean[] booleanValues(); + Stooge stooge(); + Stooge[] stooges(); + NestAnnotation nest(); + NestAnnotation[] nests(); + } + + public @interface NestAnnotation { + String string(); + String[] strings(); + Class type(); + Class[] types(); + byte byteValue(); + byte[] byteValues(); + short shortValue(); + short[] shortValues(); + int intValue(); + int[] intValues(); + char charValue(); + char[] charValues(); + long longValue(); + long[] longValues(); + float floatValue(); + float[] floatValues(); + double doubleValue(); + double[] doubleValues(); + boolean booleanValue(); + boolean[] booleanValues(); + Stooge stooge(); + Stooge[] stooges(); + } + + public static enum Stooge { + MOE, LARRY, CURLY, JOE, SHEMP; + } + + private Field field1; + private Field field2; + private Field field3; + private Field field4; + + @Before + public void setup() throws Exception { + field1 = getClass().getDeclaredField("dummy1"); + field2 = getClass().getDeclaredField("dummy2"); + field3 = getClass().getDeclaredField("dummy3"); + field4 = getClass().getDeclaredField("dummy4"); + } + + @Test + public void testEquivalence() { + assertTrue(AnnotationUtils.equals(field1.getAnnotation(TestAnnotation.class), field2.getAnnotation(TestAnnotation.class))); + assertTrue(AnnotationUtils.equals(field2.getAnnotation(TestAnnotation.class), field1.getAnnotation(TestAnnotation.class))); + } + + @Test + public void testSameInstance() { + assertTrue(AnnotationUtils.equals(field1.getAnnotation(TestAnnotation.class), field1.getAnnotation(TestAnnotation.class))); + } + + @Test + public void testNonEquivalentAnnotationsOfSameType() { + assertFalse(AnnotationUtils.equals(field1.getAnnotation(TestAnnotation.class), field3.getAnnotation(TestAnnotation.class))); + assertFalse(AnnotationUtils.equals(field3.getAnnotation(TestAnnotation.class), field1.getAnnotation(TestAnnotation.class))); + } + + @Test + public void testAnnotationsOfDifferingTypes() { + assertFalse(AnnotationUtils.equals(field1.getAnnotation(TestAnnotation.class), field4.getAnnotation(NestAnnotation.class))); + assertFalse(AnnotationUtils.equals(field4.getAnnotation(NestAnnotation.class), field1.getAnnotation(TestAnnotation.class))); + } + + @Test + public void testOneArgNull() { + assertFalse(AnnotationUtils.equals(field1.getAnnotation(TestAnnotation.class), null)); + assertFalse(AnnotationUtils.equals(null, field1.getAnnotation(TestAnnotation.class))); + } + + @Test + public void testBothArgsNull() { + assertTrue(AnnotationUtils.equals(null, null)); + } + + @Test + public void testIsValidAnnotationMemberType() { + for (Class type : new Class[] { byte.class, short.class, int.class, char.class, + long.class, float.class, double.class, boolean.class, String.class, Class.class, + NestAnnotation.class, TestAnnotation.class, Stooge.class, ElementType.class }) { + assertTrue(AnnotationUtils.isValidAnnotationMemberType(type)); + assertTrue(AnnotationUtils.isValidAnnotationMemberType(Array.newInstance(type, 0) + .getClass())); + } + for (Class type : new Class[] { Object.class, Map.class, Collection.class }) { + assertFalse(AnnotationUtils.isValidAnnotationMemberType(type)); + assertFalse(AnnotationUtils.isValidAnnotationMemberType(Array.newInstance(type, 0) + .getClass())); + } + } + + @Test(timeout = 666000) + public void testGeneratedAnnotationEquivalentToRealAnnotation() throws Exception { + final Test real = getClass().getDeclaredMethod( + "testGeneratedAnnotationEquivalentToRealAnnotation").getAnnotation(Test.class); + + InvocationHandler generatedTestInvocationHandler = new InvocationHandler() { + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + if ("equals".equals(method.getName()) && method.getParameterTypes().length == 1) { + return Boolean.valueOf(proxy == args[0]); + } + if ("hashCode".equals(method.getName()) && method.getParameterTypes().length == 0) { + return Integer.valueOf(System.identityHashCode(proxy)); + } + if ("toString".equals(method.getName()) && method.getParameterTypes().length == 0) { + return "Test proxy"; + } + return method.invoke(real, args); + } + }; + + Test generated = (Test) Proxy.newProxyInstance(Thread.currentThread() + .getContextClassLoader(), new Class[] { Test.class }, + generatedTestInvocationHandler); + assertTrue(real.equals(generated)); + assertFalse(generated.equals(real)); + assertTrue(AnnotationUtils.equals(generated, real)); + assertTrue(AnnotationUtils.equals(real, generated)); + + Test generated2 = (Test) Proxy.newProxyInstance(Thread.currentThread() + .getContextClassLoader(), new Class[] { Test.class }, + generatedTestInvocationHandler); + assertFalse(generated.equals(generated2)); + assertFalse(generated2.equals(generated)); + assertTrue(AnnotationUtils.equals(generated, generated2)); + assertTrue(AnnotationUtils.equals(generated2, generated)); + } + + @Test(timeout = 666000) + public void testHashCode() throws Exception { + final Test test = getClass().getDeclaredMethod("testHashCode").getAnnotation(Test.class); + assertEquals(test.hashCode(), AnnotationUtils.hashCode(test)); + final TestAnnotation testAnnotation1 = field1.getAnnotation(TestAnnotation.class); + assertEquals(testAnnotation1.hashCode(), AnnotationUtils.hashCode(testAnnotation1)); + final TestAnnotation testAnnotation3 = field3.getAnnotation(TestAnnotation.class); + assertEquals(testAnnotation3.hashCode(), AnnotationUtils.hashCode(testAnnotation3)); + } + + @Test(timeout = 666000) + public void testToString() throws Exception { + final Test testAnno = getClass().getDeclaredMethod("testToString") + .getAnnotation(Test.class); + String toString = AnnotationUtils.toString(testAnno); + assertTrue(toString.startsWith("@org.junit.Test(")); + assertTrue(toString.endsWith(")")); + assertTrue(toString.contains("expected=class org.junit.Test$None")); + assertTrue(toString.contains("timeout=666000")); + assertTrue(toString.contains(", ")); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/ArrayUtils.java b/ApacheCommonsLang/org/apache/commons/lang3/ArrayUtils.java new file mode 100644 index 0000000..c9241c5 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/ArrayUtils.java @@ -0,0 +1,5796 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3; + +import java.lang.reflect.Array; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; + +import org.apache.commons.lang3.builder.EqualsBuilder; +import org.apache.commons.lang3.builder.HashCodeBuilder; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import org.apache.commons.lang3.mutable.MutableInt; + +/** + *

Operations on arrays, primitive arrays (like {@code int[]}) and + * primitive wrapper arrays (like {@code Integer[]}).

+ * + *

This class tries to handle {@code null} input gracefully. + * An exception will not be thrown for a {@code null} + * array input. However, an Object array that contains a {@code null} + * element may throw an exception. Each method documents its behaviour.

+ * + *

#ThreadSafe#

+ * @since 2.0 + * @version $Id: ArrayUtils.java 1154216 2011-08-05 13:57:16Z mbenson $ + */ +public class ArrayUtils { + + /** + * An empty immutable {@code Object} array. + */ + public static final Object[] EMPTY_OBJECT_ARRAY = new Object[0]; + /** + * An empty immutable {@code Class} array. + */ + public static final Class[] EMPTY_CLASS_ARRAY = new Class[0]; + /** + * An empty immutable {@code String} array. + */ + public static final String[] EMPTY_STRING_ARRAY = new String[0]; + /** + * An empty immutable {@code long} array. + */ + public static final long[] EMPTY_LONG_ARRAY = new long[0]; + /** + * An empty immutable {@code Long} array. + */ + public static final Long[] EMPTY_LONG_OBJECT_ARRAY = new Long[0]; + /** + * An empty immutable {@code int} array. + */ + public static final int[] EMPTY_INT_ARRAY = new int[0]; + /** + * An empty immutable {@code Integer} array. + */ + public static final Integer[] EMPTY_INTEGER_OBJECT_ARRAY = new Integer[0]; + /** + * An empty immutable {@code short} array. + */ + public static final short[] EMPTY_SHORT_ARRAY = new short[0]; + /** + * An empty immutable {@code Short} array. + */ + public static final Short[] EMPTY_SHORT_OBJECT_ARRAY = new Short[0]; + /** + * An empty immutable {@code byte} array. + */ + public static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; + /** + * An empty immutable {@code Byte} array. + */ + public static final Byte[] EMPTY_BYTE_OBJECT_ARRAY = new Byte[0]; + /** + * An empty immutable {@code double} array. + */ + public static final double[] EMPTY_DOUBLE_ARRAY = new double[0]; + /** + * An empty immutable {@code Double} array. + */ + public static final Double[] EMPTY_DOUBLE_OBJECT_ARRAY = new Double[0]; + /** + * An empty immutable {@code float} array. + */ + public static final float[] EMPTY_FLOAT_ARRAY = new float[0]; + /** + * An empty immutable {@code Float} array. + */ + public static final Float[] EMPTY_FLOAT_OBJECT_ARRAY = new Float[0]; + /** + * An empty immutable {@code boolean} array. + */ + public static final boolean[] EMPTY_BOOLEAN_ARRAY = new boolean[0]; + /** + * An empty immutable {@code Boolean} array. + */ + public static final Boolean[] EMPTY_BOOLEAN_OBJECT_ARRAY = new Boolean[0]; + /** + * An empty immutable {@code char} array. + */ + public static final char[] EMPTY_CHAR_ARRAY = new char[0]; + /** + * An empty immutable {@code Character} array. + */ + public static final Character[] EMPTY_CHARACTER_OBJECT_ARRAY = new Character[0]; + + /** + * The index value when an element is not found in a list or array: {@code -1}. + * This value is returned by methods in this class and can also be used in comparisons with values returned by + * various method from {@link java.util.List}. + */ + public static final int INDEX_NOT_FOUND = -1; + + /** + *

ArrayUtils instances should NOT be constructed in standard programming. + * Instead, the class should be used as ArrayUtils.clone(new int[] {2}).

+ * + *

This constructor is public to permit tools that require a JavaBean instance + * to operate.

+ */ + public ArrayUtils() { + super(); + } + + + // NOTE: Cannot use {@code} to enclose text which includes {}, but is OK + + + // Basic methods handling multi-dimensional arrays + //----------------------------------------------------------------------- + /** + *

Outputs an array as a String, treating {@code null} as an empty array.

+ * + *

Multi-dimensional arrays are handled correctly, including + * multi-dimensional primitive arrays.

+ * + *

The format is that of Java source code, for example {a,b}.

+ * + * @param array the array to get a toString for, may be {@code null} + * @return a String representation of the array, '{}' if null array input + */ + public static String toString(Object array) { + return toString(array, "{}"); + } + + /** + *

Outputs an array as a String handling {@code null}s.

+ * + *

Multi-dimensional arrays are handled correctly, including + * multi-dimensional primitive arrays.

+ * + *

The format is that of Java source code, for example {a,b}.

+ * + * @param array the array to get a toString for, may be {@code null} + * @param stringIfNull the String to return if the array is {@code null} + * @return a String representation of the array + */ + public static String toString(Object array, String stringIfNull) { + if (array == null) { + return stringIfNull; + } + return new ToStringBuilder(array, ToStringStyle.SIMPLE_STYLE).append(array).toString(); + } + + /** + *

Get a hash code for an array handling multi-dimensional arrays correctly.

+ * + *

Multi-dimensional primitive arrays are also handled correctly by this method.

+ * + * @param array the array to get a hash code for, {@code null} returns zero + * @return a hash code for the array + */ + public static int hashCode(Object array) { + return new HashCodeBuilder().append(array).toHashCode(); + } + + /** + *

Compares two arrays, using equals(), handling multi-dimensional arrays + * correctly.

+ * + *

Multi-dimensional primitive arrays are also handled correctly by this method.

+ * + * @param array1 the left hand array to compare, may be {@code null} + * @param array2 the right hand array to compare, may be {@code null} + * @return {@code true} if the arrays are equal + */ + public static boolean isEquals(Object array1, Object array2) { + return new EqualsBuilder().append(array1, array2).isEquals(); + } + + // To map + //----------------------------------------------------------------------- + /** + *

Converts the given array into a {@link java.util.Map}. Each element of the array + * must be either a {@link java.util.Map.Entry} or an Array, containing at least two + * elements, where the first element is used as key and the second as + * value.

+ * + *

This method can be used to initialize:

+ *
+     * // Create a Map mapping colors.
+     * Map colorMap = MapUtils.toMap(new String[][] {{
+     *     {"RED", "#FF0000"},
+     *     {"GREEN", "#00FF00"},
+     *     {"BLUE", "#0000FF"}});
+     * 
+ * + *

This method returns {@code null} for a {@code null} input array.

+ * + * @param array an array whose elements are either a {@link java.util.Map.Entry} or + * an Array containing at least two elements, may be {@code null} + * @return a {@code Map} that was created from the array + * @throws IllegalArgumentException if one element of this Array is + * itself an Array containing less then two elements + * @throws IllegalArgumentException if the array contains elements other + * than {@link java.util.Map.Entry} and an Array + */ + public static Map toMap(Object[] array) { + if (array == null) { + return null; + } + final Map map = new HashMap((int) (array.length * 1.5)); + for (int i = 0; i < array.length; i++) { + Object object = array[i]; + if (object instanceof Map.Entry) { + Map.Entry entry = (Map.Entry) object; + map.put(entry.getKey(), entry.getValue()); + } else if (object instanceof Object[]) { + Object[] entry = (Object[]) object; + if (entry.length < 2) { + throw new IllegalArgumentException("Array element " + i + ", '" + + object + + "', has a length less than 2"); + } + map.put(entry[0], entry[1]); + } else { + throw new IllegalArgumentException("Array element " + i + ", '" + + object + + "', is neither of type Map.Entry nor an Array"); + } + } + return map; + } + + // Generic array + //----------------------------------------------------------------------- + /** + *

Create a type-safe generic array.

+ * + *

The Java language does not allow an array to be created from a generic type:

+ * + *
+    public static <T> T[] createAnArray(int size) {
+        return new T[size]; // compiler error here
+    }
+    public static <T> T[] createAnArray(int size) {
+        return (T[])new Object[size]; // ClassCastException at runtime
+    }
+     * 
+ * + *

Therefore new arrays of generic types can be created with this method. + * For example, an array of Strings can be created:

+ * + *
+    String[] array = ArrayUtils.toArray("1", "2");
+    String[] emptyArray = ArrayUtils.<String>toArray();
+     * 
+ * + *

The method is typically used in scenarios, where the caller itself uses generic types + * that have to be combined into an array.

+ * + *

Note, this method makes only sense to provide arguments of the same type so that the + * compiler can deduce the type of the array itself. While it is possible to select the + * type explicitly like in + * Number[] array = ArrayUtils.<Number>toArray(Integer.valueOf(42), Double.valueOf(Math.PI)), + * there is no real advantage when compared to + * new Number[] {Integer.valueOf(42), Double.valueOf(Math.PI)}.

+ * + * @param the array's element type + * @param items the varargs array items, null allowed + * @return the array, not null unless a null array is passed in + * @since 3.0 + */ + public static T[] toArray(final T... items) { + return items; + } + + // Clone + //----------------------------------------------------------------------- + /** + *

Shallow clones an array returning a typecast result and handling + * {@code null}.

+ * + *

The objects in the array are not cloned, thus there is no special + * handling for multi-dimensional arrays.

+ * + *

This method returns {@code null} for a {@code null} input array.

+ * + * @param the component type of the array + * @param array the array to shallow clone, may be {@code null} + * @return the cloned array, {@code null} if {@code null} input + */ + public static T[] clone(T[] array) { + if (array == null) { + return null; + } + return array.clone(); + } + + /** + *

Clones an array returning a typecast result and handling + * {@code null}.

+ * + *

This method returns {@code null} for a {@code null} input array.

+ * + * @param array the array to clone, may be {@code null} + * @return the cloned array, {@code null} if {@code null} input + */ + public static long[] clone(long[] array) { + if (array == null) { + return null; + } + return array.clone(); + } + + /** + *

Clones an array returning a typecast result and handling + * {@code null}.

+ * + *

This method returns {@code null} for a {@code null} input array.

+ * + * @param array the array to clone, may be {@code null} + * @return the cloned array, {@code null} if {@code null} input + */ + public static int[] clone(int[] array) { + if (array == null) { + return null; + } + return array.clone(); + } + + /** + *

Clones an array returning a typecast result and handling + * {@code null}.

+ * + *

This method returns {@code null} for a {@code null} input array.

+ * + * @param array the array to clone, may be {@code null} + * @return the cloned array, {@code null} if {@code null} input + */ + public static short[] clone(short[] array) { + if (array == null) { + return null; + } + return array.clone(); + } + + /** + *

Clones an array returning a typecast result and handling + * {@code null}.

+ * + *

This method returns {@code null} for a {@code null} input array.

+ * + * @param array the array to clone, may be {@code null} + * @return the cloned array, {@code null} if {@code null} input + */ + public static char[] clone(char[] array) { + if (array == null) { + return null; + } + return array.clone(); + } + + /** + *

Clones an array returning a typecast result and handling + * {@code null}.

+ * + *

This method returns {@code null} for a {@code null} input array.

+ * + * @param array the array to clone, may be {@code null} + * @return the cloned array, {@code null} if {@code null} input + */ + public static byte[] clone(byte[] array) { + if (array == null) { + return null; + } + return array.clone(); + } + + /** + *

Clones an array returning a typecast result and handling + * {@code null}.

+ * + *

This method returns {@code null} for a {@code null} input array.

+ * + * @param array the array to clone, may be {@code null} + * @return the cloned array, {@code null} if {@code null} input + */ + public static double[] clone(double[] array) { + if (array == null) { + return null; + } + return array.clone(); + } + + /** + *

Clones an array returning a typecast result and handling + * {@code null}.

+ * + *

This method returns {@code null} for a {@code null} input array.

+ * + * @param array the array to clone, may be {@code null} + * @return the cloned array, {@code null} if {@code null} input + */ + public static float[] clone(float[] array) { + if (array == null) { + return null; + } + return array.clone(); + } + + /** + *

Clones an array returning a typecast result and handling + * {@code null}.

+ * + *

This method returns {@code null} for a {@code null} input array.

+ * + * @param array the array to clone, may be {@code null} + * @return the cloned array, {@code null} if {@code null} input + */ + public static boolean[] clone(boolean[] array) { + if (array == null) { + return null; + } + return array.clone(); + } + + // nullToEmpty + //----------------------------------------------------------------------- + /** + *

Defensive programming technique to change a {@code null} + * reference to an empty one.

+ * + *

This method returns an empty array for a {@code null} input array.

+ * + *

As a memory optimizing technique an empty array passed in will be overridden with + * the empty {@code public static} references in this class.

+ * + * @param array the array to check for {@code null} or empty + * @return the same array, {@code public static} empty array if {@code null} or empty input + * @since 2.5 + */ + public static Object[] nullToEmpty(Object[] array) { + if (array == null || array.length == 0) { + return EMPTY_OBJECT_ARRAY; + } + return array; + } + + /** + *

Defensive programming technique to change a {@code null} + * reference to an empty one.

+ * + *

This method returns an empty array for a {@code null} input array.

+ * + *

As a memory optimizing technique an empty array passed in will be overridden with + * the empty {@code public static} references in this class.

+ * + * @param array the array to check for {@code null} or empty + * @return the same array, {@code public static} empty array if {@code null} or empty input + * @since 2.5 + */ + public static String[] nullToEmpty(String[] array) { + if (array == null || array.length == 0) { + return EMPTY_STRING_ARRAY; + } + return array; + } + + /** + *

Defensive programming technique to change a {@code null} + * reference to an empty one.

+ * + *

This method returns an empty array for a {@code null} input array.

+ * + *

As a memory optimizing technique an empty array passed in will be overridden with + * the empty {@code public static} references in this class.

+ * + * @param array the array to check for {@code null} or empty + * @return the same array, {@code public static} empty array if {@code null} or empty input + * @since 2.5 + */ + public static long[] nullToEmpty(long[] array) { + if (array == null || array.length == 0) { + return EMPTY_LONG_ARRAY; + } + return array; + } + + /** + *

Defensive programming technique to change a {@code null} + * reference to an empty one.

+ * + *

This method returns an empty array for a {@code null} input array.

+ * + *

As a memory optimizing technique an empty array passed in will be overridden with + * the empty {@code public static} references in this class.

+ * + * @param array the array to check for {@code null} or empty + * @return the same array, {@code public static} empty array if {@code null} or empty input + * @since 2.5 + */ + public static int[] nullToEmpty(int[] array) { + if (array == null || array.length == 0) { + return EMPTY_INT_ARRAY; + } + return array; + } + + /** + *

Defensive programming technique to change a {@code null} + * reference to an empty one.

+ * + *

This method returns an empty array for a {@code null} input array.

+ * + *

As a memory optimizing technique an empty array passed in will be overridden with + * the empty {@code public static} references in this class.

+ * + * @param array the array to check for {@code null} or empty + * @return the same array, {@code public static} empty array if {@code null} or empty input + * @since 2.5 + */ + public static short[] nullToEmpty(short[] array) { + if (array == null || array.length == 0) { + return EMPTY_SHORT_ARRAY; + } + return array; + } + + /** + *

Defensive programming technique to change a {@code null} + * reference to an empty one.

+ * + *

This method returns an empty array for a {@code null} input array.

+ * + *

As a memory optimizing technique an empty array passed in will be overridden with + * the empty {@code public static} references in this class.

+ * + * @param array the array to check for {@code null} or empty + * @return the same array, {@code public static} empty array if {@code null} or empty input + * @since 2.5 + */ + public static char[] nullToEmpty(char[] array) { + if (array == null || array.length == 0) { + return EMPTY_CHAR_ARRAY; + } + return array; + } + + /** + *

Defensive programming technique to change a {@code null} + * reference to an empty one.

+ * + *

This method returns an empty array for a {@code null} input array.

+ * + *

As a memory optimizing technique an empty array passed in will be overridden with + * the empty {@code public static} references in this class.

+ * + * @param array the array to check for {@code null} or empty + * @return the same array, {@code public static} empty array if {@code null} or empty input + * @since 2.5 + */ + public static byte[] nullToEmpty(byte[] array) { + if (array == null || array.length == 0) { + return EMPTY_BYTE_ARRAY; + } + return array; + } + + /** + *

Defensive programming technique to change a {@code null} + * reference to an empty one.

+ * + *

This method returns an empty array for a {@code null} input array.

+ * + *

As a memory optimizing technique an empty array passed in will be overridden with + * the empty {@code public static} references in this class.

+ * + * @param array the array to check for {@code null} or empty + * @return the same array, {@code public static} empty array if {@code null} or empty input + * @since 2.5 + */ + public static double[] nullToEmpty(double[] array) { + if (array == null || array.length == 0) { + return EMPTY_DOUBLE_ARRAY; + } + return array; + } + + /** + *

Defensive programming technique to change a {@code null} + * reference to an empty one.

+ * + *

This method returns an empty array for a {@code null} input array.

+ * + *

As a memory optimizing technique an empty array passed in will be overridden with + * the empty {@code public static} references in this class.

+ * + * @param array the array to check for {@code null} or empty + * @return the same array, {@code public static} empty array if {@code null} or empty input + * @since 2.5 + */ + public static float[] nullToEmpty(float[] array) { + if (array == null || array.length == 0) { + return EMPTY_FLOAT_ARRAY; + } + return array; + } + + /** + *

Defensive programming technique to change a {@code null} + * reference to an empty one.

+ * + *

This method returns an empty array for a {@code null} input array.

+ * + *

As a memory optimizing technique an empty array passed in will be overridden with + * the empty {@code public static} references in this class.

+ * + * @param array the array to check for {@code null} or empty + * @return the same array, {@code public static} empty array if {@code null} or empty input + * @since 2.5 + */ + public static boolean[] nullToEmpty(boolean[] array) { + if (array == null || array.length == 0) { + return EMPTY_BOOLEAN_ARRAY; + } + return array; + } + + /** + *

Defensive programming technique to change a {@code null} + * reference to an empty one.

+ * + *

This method returns an empty array for a {@code null} input array.

+ * + *

As a memory optimizing technique an empty array passed in will be overridden with + * the empty {@code public static} references in this class.

+ * + * @param array the array to check for {@code null} or empty + * @return the same array, {@code public static} empty array if {@code null} or empty input + * @since 2.5 + */ + public static Long[] nullToEmpty(Long[] array) { + if (array == null || array.length == 0) { + return EMPTY_LONG_OBJECT_ARRAY; + } + return array; + } + + /** + *

Defensive programming technique to change a {@code null} + * reference to an empty one.

+ * + *

This method returns an empty array for a {@code null} input array.

+ * + *

As a memory optimizing technique an empty array passed in will be overridden with + * the empty {@code public static} references in this class.

+ * + * @param array the array to check for {@code null} or empty + * @return the same array, {@code public static} empty array if {@code null} or empty input + * @since 2.5 + */ + public static Integer[] nullToEmpty(Integer[] array) { + if (array == null || array.length == 0) { + return EMPTY_INTEGER_OBJECT_ARRAY; + } + return array; + } + + /** + *

Defensive programming technique to change a {@code null} + * reference to an empty one.

+ * + *

This method returns an empty array for a {@code null} input array.

+ * + *

As a memory optimizing technique an empty array passed in will be overridden with + * the empty {@code public static} references in this class.

+ * + * @param array the array to check for {@code null} or empty + * @return the same array, {@code public static} empty array if {@code null} or empty input + * @since 2.5 + */ + public static Short[] nullToEmpty(Short[] array) { + if (array == null || array.length == 0) { + return EMPTY_SHORT_OBJECT_ARRAY; + } + return array; + } + + /** + *

Defensive programming technique to change a {@code null} + * reference to an empty one.

+ * + *

This method returns an empty array for a {@code null} input array.

+ * + *

As a memory optimizing technique an empty array passed in will be overridden with + * the empty {@code public static} references in this class.

+ * + * @param array the array to check for {@code null} or empty + * @return the same array, {@code public static} empty array if {@code null} or empty input + * @since 2.5 + */ + public static Character[] nullToEmpty(Character[] array) { + if (array == null || array.length == 0) { + return EMPTY_CHARACTER_OBJECT_ARRAY; + } + return array; + } + + /** + *

Defensive programming technique to change a {@code null} + * reference to an empty one.

+ * + *

This method returns an empty array for a {@code null} input array.

+ * + *

As a memory optimizing technique an empty array passed in will be overridden with + * the empty {@code public static} references in this class.

+ * + * @param array the array to check for {@code null} or empty + * @return the same array, {@code public static} empty array if {@code null} or empty input + * @since 2.5 + */ + public static Byte[] nullToEmpty(Byte[] array) { + if (array == null || array.length == 0) { + return EMPTY_BYTE_OBJECT_ARRAY; + } + return array; + } + + /** + *

Defensive programming technique to change a {@code null} + * reference to an empty one.

+ * + *

This method returns an empty array for a {@code null} input array.

+ * + *

As a memory optimizing technique an empty array passed in will be overridden with + * the empty {@code public static} references in this class.

+ * + * @param array the array to check for {@code null} or empty + * @return the same array, {@code public static} empty array if {@code null} or empty input + * @since 2.5 + */ + public static Double[] nullToEmpty(Double[] array) { + if (array == null || array.length == 0) { + return EMPTY_DOUBLE_OBJECT_ARRAY; + } + return array; + } + + /** + *

Defensive programming technique to change a {@code null} + * reference to an empty one.

+ * + *

This method returns an empty array for a {@code null} input array.

+ * + *

As a memory optimizing technique an empty array passed in will be overridden with + * the empty {@code public static} references in this class.

+ * + * @param array the array to check for {@code null} or empty + * @return the same array, {@code public static} empty array if {@code null} or empty input + * @since 2.5 + */ + public static Float[] nullToEmpty(Float[] array) { + if (array == null || array.length == 0) { + return EMPTY_FLOAT_OBJECT_ARRAY; + } + return array; + } + + /** + *

Defensive programming technique to change a {@code null} + * reference to an empty one.

+ * + *

This method returns an empty array for a {@code null} input array.

+ * + *

As a memory optimizing technique an empty array passed in will be overridden with + * the empty {@code public static} references in this class.

+ * + * @param array the array to check for {@code null} or empty + * @return the same array, {@code public static} empty array if {@code null} or empty input + * @since 2.5 + */ + public static Boolean[] nullToEmpty(Boolean[] array) { + if (array == null || array.length == 0) { + return EMPTY_BOOLEAN_OBJECT_ARRAY; + } + return array; + } + + // Subarrays + //----------------------------------------------------------------------- + /** + *

Produces a new array containing the elements between + * the start and end indices.

+ * + *

The start index is inclusive, the end index exclusive. + * Null array input produces null output.

+ * + *

The component type of the subarray is always the same as + * that of the input array. Thus, if the input is an array of type + * {@code Date}, the following usage is envisaged:

+ * + *
+     * Date[] someDates = (Date[])ArrayUtils.subarray(allDates, 2, 5);
+     * 
+ * + * @param the component type of the array + * @param array the array + * @param startIndexInclusive the starting index. Undervalue (<0) + * is promoted to 0, overvalue (>array.length) results + * in an empty array. + * @param endIndexExclusive elements up to endIndex-1 are present in the + * returned subarray. Undervalue (< startIndex) produces + * empty array, overvalue (>array.length) is demoted to + * array length. + * @return a new array containing the elements between + * the start and end indices. + * @since 2.1 + */ + public static T[] subarray(T[] array, int startIndexInclusive, int endIndexExclusive) { + if (array == null) { + return null; + } + if (startIndexInclusive < 0) { + startIndexInclusive = 0; + } + if (endIndexExclusive > array.length) { + endIndexExclusive = array.length; + } + int newSize = endIndexExclusive - startIndexInclusive; + Class type = array.getClass().getComponentType(); + if (newSize <= 0) { + @SuppressWarnings("unchecked") // OK, because array is of type T + final T[] emptyArray = (T[]) Array.newInstance(type, 0); + return emptyArray; + } + @SuppressWarnings("unchecked") // OK, because array is of type T + T[] subarray = (T[]) Array.newInstance(type, newSize); + System.arraycopy(array, startIndexInclusive, subarray, 0, newSize); + return subarray; + } + + /** + *

Produces a new {@code long} array containing the elements + * between the start and end indices.

+ * + *

The start index is inclusive, the end index exclusive. + * Null array input produces null output.

+ * + * @param array the array + * @param startIndexInclusive the starting index. Undervalue (<0) + * is promoted to 0, overvalue (>array.length) results + * in an empty array. + * @param endIndexExclusive elements up to endIndex-1 are present in the + * returned subarray. Undervalue (< startIndex) produces + * empty array, overvalue (>array.length) is demoted to + * array length. + * @return a new array containing the elements between + * the start and end indices. + * @since 2.1 + */ + public static long[] subarray(long[] array, int startIndexInclusive, int endIndexExclusive) { + if (array == null) { + return null; + } + if (startIndexInclusive < 0) { + startIndexInclusive = 0; + } + if (endIndexExclusive > array.length) { + endIndexExclusive = array.length; + } + int newSize = endIndexExclusive - startIndexInclusive; + if (newSize <= 0) { + return EMPTY_LONG_ARRAY; + } + + long[] subarray = new long[newSize]; + System.arraycopy(array, startIndexInclusive, subarray, 0, newSize); + return subarray; + } + + /** + *

Produces a new {@code int} array containing the elements + * between the start and end indices.

+ * + *

The start index is inclusive, the end index exclusive. + * Null array input produces null output.

+ * + * @param array the array + * @param startIndexInclusive the starting index. Undervalue (<0) + * is promoted to 0, overvalue (>array.length) results + * in an empty array. + * @param endIndexExclusive elements up to endIndex-1 are present in the + * returned subarray. Undervalue (< startIndex) produces + * empty array, overvalue (>array.length) is demoted to + * array length. + * @return a new array containing the elements between + * the start and end indices. + * @since 2.1 + */ + public static int[] subarray(int[] array, int startIndexInclusive, int endIndexExclusive) { + if (array == null) { + return null; + } + if (startIndexInclusive < 0) { + startIndexInclusive = 0; + } + if (endIndexExclusive > array.length) { + endIndexExclusive = array.length; + } + int newSize = endIndexExclusive - startIndexInclusive; + if (newSize <= 0) { + return EMPTY_INT_ARRAY; + } + + int[] subarray = new int[newSize]; + System.arraycopy(array, startIndexInclusive, subarray, 0, newSize); + return subarray; + } + + /** + *

Produces a new {@code short} array containing the elements + * between the start and end indices.

+ * + *

The start index is inclusive, the end index exclusive. + * Null array input produces null output.

+ * + * @param array the array + * @param startIndexInclusive the starting index. Undervalue (<0) + * is promoted to 0, overvalue (>array.length) results + * in an empty array. + * @param endIndexExclusive elements up to endIndex-1 are present in the + * returned subarray. Undervalue (< startIndex) produces + * empty array, overvalue (>array.length) is demoted to + * array length. + * @return a new array containing the elements between + * the start and end indices. + * @since 2.1 + */ + public static short[] subarray(short[] array, int startIndexInclusive, int endIndexExclusive) { + if (array == null) { + return null; + } + if (startIndexInclusive < 0) { + startIndexInclusive = 0; + } + if (endIndexExclusive > array.length) { + endIndexExclusive = array.length; + } + int newSize = endIndexExclusive - startIndexInclusive; + if (newSize <= 0) { + return EMPTY_SHORT_ARRAY; + } + + short[] subarray = new short[newSize]; + System.arraycopy(array, startIndexInclusive, subarray, 0, newSize); + return subarray; + } + + /** + *

Produces a new {@code char} array containing the elements + * between the start and end indices.

+ * + *

The start index is inclusive, the end index exclusive. + * Null array input produces null output.

+ * + * @param array the array + * @param startIndexInclusive the starting index. Undervalue (<0) + * is promoted to 0, overvalue (>array.length) results + * in an empty array. + * @param endIndexExclusive elements up to endIndex-1 are present in the + * returned subarray. Undervalue (< startIndex) produces + * empty array, overvalue (>array.length) is demoted to + * array length. + * @return a new array containing the elements between + * the start and end indices. + * @since 2.1 + */ + public static char[] subarray(char[] array, int startIndexInclusive, int endIndexExclusive) { + if (array == null) { + return null; + } + if (startIndexInclusive < 0) { + startIndexInclusive = 0; + } + if (endIndexExclusive > array.length) { + endIndexExclusive = array.length; + } + int newSize = endIndexExclusive - startIndexInclusive; + if (newSize <= 0) { + return EMPTY_CHAR_ARRAY; + } + + char[] subarray = new char[newSize]; + System.arraycopy(array, startIndexInclusive, subarray, 0, newSize); + return subarray; + } + + /** + *

Produces a new {@code byte} array containing the elements + * between the start and end indices.

+ * + *

The start index is inclusive, the end index exclusive. + * Null array input produces null output.

+ * + * @param array the array + * @param startIndexInclusive the starting index. Undervalue (<0) + * is promoted to 0, overvalue (>array.length) results + * in an empty array. + * @param endIndexExclusive elements up to endIndex-1 are present in the + * returned subarray. Undervalue (< startIndex) produces + * empty array, overvalue (>array.length) is demoted to + * array length. + * @return a new array containing the elements between + * the start and end indices. + * @since 2.1 + */ + public static byte[] subarray(byte[] array, int startIndexInclusive, int endIndexExclusive) { + if (array == null) { + return null; + } + if (startIndexInclusive < 0) { + startIndexInclusive = 0; + } + if (endIndexExclusive > array.length) { + endIndexExclusive = array.length; + } + int newSize = endIndexExclusive - startIndexInclusive; + if (newSize <= 0) { + return EMPTY_BYTE_ARRAY; + } + + byte[] subarray = new byte[newSize]; + System.arraycopy(array, startIndexInclusive, subarray, 0, newSize); + return subarray; + } + + /** + *

Produces a new {@code double} array containing the elements + * between the start and end indices.

+ * + *

The start index is inclusive, the end index exclusive. + * Null array input produces null output.

+ * + * @param array the array + * @param startIndexInclusive the starting index. Undervalue (<0) + * is promoted to 0, overvalue (>array.length) results + * in an empty array. + * @param endIndexExclusive elements up to endIndex-1 are present in the + * returned subarray. Undervalue (< startIndex) produces + * empty array, overvalue (>array.length) is demoted to + * array length. + * @return a new array containing the elements between + * the start and end indices. + * @since 2.1 + */ + public static double[] subarray(double[] array, int startIndexInclusive, int endIndexExclusive) { + if (array == null) { + return null; + } + if (startIndexInclusive < 0) { + startIndexInclusive = 0; + } + if (endIndexExclusive > array.length) { + endIndexExclusive = array.length; + } + int newSize = endIndexExclusive - startIndexInclusive; + if (newSize <= 0) { + return EMPTY_DOUBLE_ARRAY; + } + + double[] subarray = new double[newSize]; + System.arraycopy(array, startIndexInclusive, subarray, 0, newSize); + return subarray; + } + + /** + *

Produces a new {@code float} array containing the elements + * between the start and end indices.

+ * + *

The start index is inclusive, the end index exclusive. + * Null array input produces null output.

+ * + * @param array the array + * @param startIndexInclusive the starting index. Undervalue (<0) + * is promoted to 0, overvalue (>array.length) results + * in an empty array. + * @param endIndexExclusive elements up to endIndex-1 are present in the + * returned subarray. Undervalue (< startIndex) produces + * empty array, overvalue (>array.length) is demoted to + * array length. + * @return a new array containing the elements between + * the start and end indices. + * @since 2.1 + */ + public static float[] subarray(float[] array, int startIndexInclusive, int endIndexExclusive) { + if (array == null) { + return null; + } + if (startIndexInclusive < 0) { + startIndexInclusive = 0; + } + if (endIndexExclusive > array.length) { + endIndexExclusive = array.length; + } + int newSize = endIndexExclusive - startIndexInclusive; + if (newSize <= 0) { + return EMPTY_FLOAT_ARRAY; + } + + float[] subarray = new float[newSize]; + System.arraycopy(array, startIndexInclusive, subarray, 0, newSize); + return subarray; + } + + /** + *

Produces a new {@code boolean} array containing the elements + * between the start and end indices.

+ * + *

The start index is inclusive, the end index exclusive. + * Null array input produces null output.

+ * + * @param array the array + * @param startIndexInclusive the starting index. Undervalue (<0) + * is promoted to 0, overvalue (>array.length) results + * in an empty array. + * @param endIndexExclusive elements up to endIndex-1 are present in the + * returned subarray. Undervalue (< startIndex) produces + * empty array, overvalue (>array.length) is demoted to + * array length. + * @return a new array containing the elements between + * the start and end indices. + * @since 2.1 + */ + public static boolean[] subarray(boolean[] array, int startIndexInclusive, int endIndexExclusive) { + if (array == null) { + return null; + } + if (startIndexInclusive < 0) { + startIndexInclusive = 0; + } + if (endIndexExclusive > array.length) { + endIndexExclusive = array.length; + } + int newSize = endIndexExclusive - startIndexInclusive; + if (newSize <= 0) { + return EMPTY_BOOLEAN_ARRAY; + } + + boolean[] subarray = new boolean[newSize]; + System.arraycopy(array, startIndexInclusive, subarray, 0, newSize); + return subarray; + } + + // Is same length + //----------------------------------------------------------------------- + /** + *

Checks whether two arrays are the same length, treating + * {@code null} arrays as length {@code 0}. + * + *

Any multi-dimensional aspects of the arrays are ignored.

+ * + * @param array1 the first array, may be {@code null} + * @param array2 the second array, may be {@code null} + * @return {@code true} if length of arrays matches, treating + * {@code null} as an empty array + */ + public static boolean isSameLength(Object[] array1, Object[] array2) { + if ((array1 == null && array2 != null && array2.length > 0) || + (array2 == null && array1 != null && array1.length > 0) || + (array1 != null && array2 != null && array1.length != array2.length)) { + return false; + } + return true; + } + + /** + *

Checks whether two arrays are the same length, treating + * {@code null} arrays as length {@code 0}.

+ * + * @param array1 the first array, may be {@code null} + * @param array2 the second array, may be {@code null} + * @return {@code true} if length of arrays matches, treating + * {@code null} as an empty array + */ + public static boolean isSameLength(long[] array1, long[] array2) { + if ((array1 == null && array2 != null && array2.length > 0) || + (array2 == null && array1 != null && array1.length > 0) || + (array1 != null && array2 != null && array1.length != array2.length)) { + return false; + } + return true; + } + + /** + *

Checks whether two arrays are the same length, treating + * {@code null} arrays as length {@code 0}.

+ * + * @param array1 the first array, may be {@code null} + * @param array2 the second array, may be {@code null} + * @return {@code true} if length of arrays matches, treating + * {@code null} as an empty array + */ + public static boolean isSameLength(int[] array1, int[] array2) { + if ((array1 == null && array2 != null && array2.length > 0) || + (array2 == null && array1 != null && array1.length > 0) || + (array1 != null && array2 != null && array1.length != array2.length)) { + return false; + } + return true; + } + + /** + *

Checks whether two arrays are the same length, treating + * {@code null} arrays as length {@code 0}.

+ * + * @param array1 the first array, may be {@code null} + * @param array2 the second array, may be {@code null} + * @return {@code true} if length of arrays matches, treating + * {@code null} as an empty array + */ + public static boolean isSameLength(short[] array1, short[] array2) { + if ((array1 == null && array2 != null && array2.length > 0) || + (array2 == null && array1 != null && array1.length > 0) || + (array1 != null && array2 != null && array1.length != array2.length)) { + return false; + } + return true; + } + + /** + *

Checks whether two arrays are the same length, treating + * {@code null} arrays as length {@code 0}.

+ * + * @param array1 the first array, may be {@code null} + * @param array2 the second array, may be {@code null} + * @return {@code true} if length of arrays matches, treating + * {@code null} as an empty array + */ + public static boolean isSameLength(char[] array1, char[] array2) { + if ((array1 == null && array2 != null && array2.length > 0) || + (array2 == null && array1 != null && array1.length > 0) || + (array1 != null && array2 != null && array1.length != array2.length)) { + return false; + } + return true; + } + + /** + *

Checks whether two arrays are the same length, treating + * {@code null} arrays as length {@code 0}.

+ * + * @param array1 the first array, may be {@code null} + * @param array2 the second array, may be {@code null} + * @return {@code true} if length of arrays matches, treating + * {@code null} as an empty array + */ + public static boolean isSameLength(byte[] array1, byte[] array2) { + if ((array1 == null && array2 != null && array2.length > 0) || + (array2 == null && array1 != null && array1.length > 0) || + (array1 != null && array2 != null && array1.length != array2.length)) { + return false; + } + return true; + } + + /** + *

Checks whether two arrays are the same length, treating + * {@code null} arrays as length {@code 0}.

+ * + * @param array1 the first array, may be {@code null} + * @param array2 the second array, may be {@code null} + * @return {@code true} if length of arrays matches, treating + * {@code null} as an empty array + */ + public static boolean isSameLength(double[] array1, double[] array2) { + if ((array1 == null && array2 != null && array2.length > 0) || + (array2 == null && array1 != null && array1.length > 0) || + (array1 != null && array2 != null && array1.length != array2.length)) { + return false; + } + return true; + } + + /** + *

Checks whether two arrays are the same length, treating + * {@code null} arrays as length {@code 0}.

+ * + * @param array1 the first array, may be {@code null} + * @param array2 the second array, may be {@code null} + * @return {@code true} if length of arrays matches, treating + * {@code null} as an empty array + */ + public static boolean isSameLength(float[] array1, float[] array2) { + if ((array1 == null && array2 != null && array2.length > 0) || + (array2 == null && array1 != null && array1.length > 0) || + (array1 != null && array2 != null && array1.length != array2.length)) { + return false; + } + return true; + } + + /** + *

Checks whether two arrays are the same length, treating + * {@code null} arrays as length {@code 0}.

+ * + * @param array1 the first array, may be {@code null} + * @param array2 the second array, may be {@code null} + * @return {@code true} if length of arrays matches, treating + * {@code null} as an empty array + */ + public static boolean isSameLength(boolean[] array1, boolean[] array2) { + if ((array1 == null && array2 != null && array2.length > 0) || + (array2 == null && array1 != null && array1.length > 0) || + (array1 != null && array2 != null && array1.length != array2.length)) { + return false; + } + return true; + } + + //----------------------------------------------------------------------- + /** + *

Returns the length of the specified array. + * This method can deal with {@code Object} arrays and with primitive arrays.

+ * + *

If the input array is {@code null}, {@code 0} is returned.

+ * + *
+     * ArrayUtils.getLength(null)            = 0
+     * ArrayUtils.getLength([])              = 0
+     * ArrayUtils.getLength([null])          = 1
+     * ArrayUtils.getLength([true, false])   = 2
+     * ArrayUtils.getLength([1, 2, 3])       = 3
+     * ArrayUtils.getLength(["a", "b", "c"]) = 3
+     * 
+ * + * @param array the array to retrieve the length from, may be null + * @return The length of the array, or {@code 0} if the array is {@code null} + * @throws IllegalArgumentException if the object arguement is not an array. + * @since 2.1 + */ + public static int getLength(Object array) { + if (array == null) { + return 0; + } + return Array.getLength(array); + } + + /** + *

Checks whether two arrays are the same type taking into account + * multi-dimensional arrays.

+ * + * @param array1 the first array, must not be {@code null} + * @param array2 the second array, must not be {@code null} + * @return {@code true} if type of arrays matches + * @throws IllegalArgumentException if either array is {@code null} + */ + public static boolean isSameType(Object array1, Object array2) { + if (array1 == null || array2 == null) { + throw new IllegalArgumentException("The Array must not be null"); + } + return array1.getClass().getName().equals(array2.getClass().getName()); + } + + // Reverse + //----------------------------------------------------------------------- + /** + *

Reverses the order of the given array.

+ * + *

There is no special handling for multi-dimensional arrays.

+ * + *

This method does nothing for a {@code null} input array.

+ * + * @param array the array to reverse, may be {@code null} + */ + public static void reverse(Object[] array) { + if (array == null) { + return; + } + int i = 0; + int j = array.length - 1; + Object tmp; + while (j > i) { + tmp = array[j]; + array[j] = array[i]; + array[i] = tmp; + j--; + i++; + } + } + + /** + *

Reverses the order of the given array.

+ * + *

This method does nothing for a {@code null} input array.

+ * + * @param array the array to reverse, may be {@code null} + */ + public static void reverse(long[] array) { + if (array == null) { + return; + } + int i = 0; + int j = array.length - 1; + long tmp; + while (j > i) { + tmp = array[j]; + array[j] = array[i]; + array[i] = tmp; + j--; + i++; + } + } + + /** + *

Reverses the order of the given array.

+ * + *

This method does nothing for a {@code null} input array.

+ * + * @param array the array to reverse, may be {@code null} + */ + public static void reverse(int[] array) { + if (array == null) { + return; + } + int i = 0; + int j = array.length - 1; + int tmp; + while (j > i) { + tmp = array[j]; + array[j] = array[i]; + array[i] = tmp; + j--; + i++; + } + } + + /** + *

Reverses the order of the given array.

+ * + *

This method does nothing for a {@code null} input array.

+ * + * @param array the array to reverse, may be {@code null} + */ + public static void reverse(short[] array) { + if (array == null) { + return; + } + int i = 0; + int j = array.length - 1; + short tmp; + while (j > i) { + tmp = array[j]; + array[j] = array[i]; + array[i] = tmp; + j--; + i++; + } + } + + /** + *

Reverses the order of the given array.

+ * + *

This method does nothing for a {@code null} input array.

+ * + * @param array the array to reverse, may be {@code null} + */ + public static void reverse(char[] array) { + if (array == null) { + return; + } + int i = 0; + int j = array.length - 1; + char tmp; + while (j > i) { + tmp = array[j]; + array[j] = array[i]; + array[i] = tmp; + j--; + i++; + } + } + + /** + *

Reverses the order of the given array.

+ * + *

This method does nothing for a {@code null} input array.

+ * + * @param array the array to reverse, may be {@code null} + */ + public static void reverse(byte[] array) { + if (array == null) { + return; + } + int i = 0; + int j = array.length - 1; + byte tmp; + while (j > i) { + tmp = array[j]; + array[j] = array[i]; + array[i] = tmp; + j--; + i++; + } + } + + /** + *

Reverses the order of the given array.

+ * + *

This method does nothing for a {@code null} input array.

+ * + * @param array the array to reverse, may be {@code null} + */ + public static void reverse(double[] array) { + if (array == null) { + return; + } + int i = 0; + int j = array.length - 1; + double tmp; + while (j > i) { + tmp = array[j]; + array[j] = array[i]; + array[i] = tmp; + j--; + i++; + } + } + + /** + *

Reverses the order of the given array.

+ * + *

This method does nothing for a {@code null} input array.

+ * + * @param array the array to reverse, may be {@code null} + */ + public static void reverse(float[] array) { + if (array == null) { + return; + } + int i = 0; + int j = array.length - 1; + float tmp; + while (j > i) { + tmp = array[j]; + array[j] = array[i]; + array[i] = tmp; + j--; + i++; + } + } + + /** + *

Reverses the order of the given array.

+ * + *

This method does nothing for a {@code null} input array.

+ * + * @param array the array to reverse, may be {@code null} + */ + public static void reverse(boolean[] array) { + if (array == null) { + return; + } + int i = 0; + int j = array.length - 1; + boolean tmp; + while (j > i) { + tmp = array[j]; + array[j] = array[i]; + array[i] = tmp; + j--; + i++; + } + } + + // IndexOf search + // ---------------------------------------------------------------------- + + // Object IndexOf + //----------------------------------------------------------------------- + /** + *

Finds the index of the given object in the array.

+ * + *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.

+ * + * @param array the array to search through for the object, may be {@code null} + * @param objectToFind the object to find, may be {@code null} + * @return the index of the object within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int indexOf(Object[] array, Object objectToFind) { + return indexOf(array, objectToFind, 0); + } + + /** + *

Finds the index of the given object in the array starting at the given index.

+ * + *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.

+ * + *

A negative startIndex is treated as zero. A startIndex larger than the array + * length will return {@link #INDEX_NOT_FOUND} ({@code -1}).

+ * + * @param array the array to search through for the object, may be {@code null} + * @param objectToFind the object to find, may be {@code null} + * @param startIndex the index to start searching at + * @return the index of the object within the array starting at the index, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int indexOf(Object[] array, Object objectToFind, int startIndex) { + if (array == null) { + return INDEX_NOT_FOUND; + } + if (startIndex < 0) { + startIndex = 0; + } + if (objectToFind == null) { + for (int i = startIndex; i < array.length; i++) { + if (array[i] == null) { + return i; + } + } + } else if (array.getClass().getComponentType().isInstance(objectToFind)) { + for (int i = startIndex; i < array.length; i++) { + if (objectToFind.equals(array[i])) { + return i; + } + } + } + return INDEX_NOT_FOUND; + } + + /** + *

Finds the last index of the given object within the array.

+ * + *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.

+ * + * @param array the array to travers backwords looking for the object, may be {@code null} + * @param objectToFind the object to find, may be {@code null} + * @return the last index of the object within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int lastIndexOf(Object[] array, Object objectToFind) { + return lastIndexOf(array, objectToFind, Integer.MAX_VALUE); + } + + /** + *

Finds the last index of the given object in the array starting at the given index.

+ * + *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.

+ * + *

A negative startIndex will return {@link #INDEX_NOT_FOUND} ({@code -1}). A startIndex larger than + * the array length will search from the end of the array.

+ * + * @param array the array to traverse for looking for the object, may be {@code null} + * @param objectToFind the object to find, may be {@code null} + * @param startIndex the start index to travers backwards from + * @return the last index of the object within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int lastIndexOf(Object[] array, Object objectToFind, int startIndex) { + if (array == null) { + return INDEX_NOT_FOUND; + } + if (startIndex < 0) { + return INDEX_NOT_FOUND; + } else if (startIndex >= array.length) { + startIndex = array.length - 1; + } + if (objectToFind == null) { + for (int i = startIndex; i >= 0; i--) { + if (array[i] == null) { + return i; + } + } + } else if (array.getClass().getComponentType().isInstance(objectToFind)) { + for (int i = startIndex; i >= 0; i--) { + if (objectToFind.equals(array[i])) { + return i; + } + } + } + return INDEX_NOT_FOUND; + } + + /** + *

Checks if the object is in the given array.

+ * + *

The method returns {@code false} if a {@code null} array is passed in.

+ * + * @param array the array to search through + * @param objectToFind the object to find + * @return {@code true} if the array contains the object + */ + public static boolean contains(Object[] array, Object objectToFind) { + return indexOf(array, objectToFind) != INDEX_NOT_FOUND; + } + + // long IndexOf + //----------------------------------------------------------------------- + /** + *

Finds the index of the given value in the array.

+ * + *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.

+ * + * @param array the array to search through for the object, may be {@code null} + * @param valueToFind the value to find + * @return the index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int indexOf(long[] array, long valueToFind) { + return indexOf(array, valueToFind, 0); + } + + /** + *

Finds the index of the given value in the array starting at the given index.

+ * + *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.

+ * + *

A negative startIndex is treated as zero. A startIndex larger than the array + * length will return {@link #INDEX_NOT_FOUND} ({@code -1}).

+ * + * @param array the array to search through for the object, may be {@code null} + * @param valueToFind the value to find + * @param startIndex the index to start searching at + * @return the index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int indexOf(long[] array, long valueToFind, int startIndex) { + if (array == null) { + return INDEX_NOT_FOUND; + } + if (startIndex < 0) { + startIndex = 0; + } + for (int i = startIndex; i < array.length; i++) { + if (valueToFind == array[i]) { + return i; + } + } + return INDEX_NOT_FOUND; + } + + /** + *

Finds the last index of the given value within the array.

+ * + *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.

+ * + * @param array the array to travers backwords looking for the object, may be {@code null} + * @param valueToFind the object to find + * @return the last index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int lastIndexOf(long[] array, long valueToFind) { + return lastIndexOf(array, valueToFind, Integer.MAX_VALUE); + } + + /** + *

Finds the last index of the given value in the array starting at the given index.

+ * + *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.

+ * + *

A negative startIndex will return {@link #INDEX_NOT_FOUND} ({@code -1}). A startIndex larger than the + * array length will search from the end of the array.

+ * + * @param array the array to traverse for looking for the object, may be {@code null} + * @param valueToFind the value to find + * @param startIndex the start index to travers backwards from + * @return the last index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int lastIndexOf(long[] array, long valueToFind, int startIndex) { + if (array == null) { + return INDEX_NOT_FOUND; + } + if (startIndex < 0) { + return INDEX_NOT_FOUND; + } else if (startIndex >= array.length) { + startIndex = array.length - 1; + } + for (int i = startIndex; i >= 0; i--) { + if (valueToFind == array[i]) { + return i; + } + } + return INDEX_NOT_FOUND; + } + + /** + *

Checks if the value is in the given array.

+ * + *

The method returns {@code false} if a {@code null} array is passed in.

+ * + * @param array the array to search through + * @param valueToFind the value to find + * @return {@code true} if the array contains the object + */ + public static boolean contains(long[] array, long valueToFind) { + return indexOf(array, valueToFind) != INDEX_NOT_FOUND; + } + + // int IndexOf + //----------------------------------------------------------------------- + /** + *

Finds the index of the given value in the array.

+ * + *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.

+ * + * @param array the array to search through for the object, may be {@code null} + * @param valueToFind the value to find + * @return the index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int indexOf(int[] array, int valueToFind) { + return indexOf(array, valueToFind, 0); + } + + /** + *

Finds the index of the given value in the array starting at the given index.

+ * + *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.

+ * + *

A negative startIndex is treated as zero. A startIndex larger than the array + * length will return {@link #INDEX_NOT_FOUND} ({@code -1}).

+ * + * @param array the array to search through for the object, may be {@code null} + * @param valueToFind the value to find + * @param startIndex the index to start searching at + * @return the index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int indexOf(int[] array, int valueToFind, int startIndex) { + if (array == null) { + return INDEX_NOT_FOUND; + } + if (startIndex < 0) { + startIndex = 0; + } + for (int i = startIndex; i < array.length; i++) { + if (valueToFind == array[i]) { + return i; + } + } + return INDEX_NOT_FOUND; + } + + /** + *

Finds the last index of the given value within the array.

+ * + *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.

+ * + * @param array the array to travers backwords looking for the object, may be {@code null} + * @param valueToFind the object to find + * @return the last index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int lastIndexOf(int[] array, int valueToFind) { + return lastIndexOf(array, valueToFind, Integer.MAX_VALUE); + } + + /** + *

Finds the last index of the given value in the array starting at the given index.

+ * + *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.

+ * + *

A negative startIndex will return {@link #INDEX_NOT_FOUND} ({@code -1}). A startIndex larger than the + * array length will search from the end of the array.

+ * + * @param array the array to traverse for looking for the object, may be {@code null} + * @param valueToFind the value to find + * @param startIndex the start index to travers backwards from + * @return the last index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int lastIndexOf(int[] array, int valueToFind, int startIndex) { + if (array == null) { + return INDEX_NOT_FOUND; + } + if (startIndex < 0) { + return INDEX_NOT_FOUND; + } else if (startIndex >= array.length) { + startIndex = array.length - 1; + } + for (int i = startIndex; i >= 0; i--) { + if (valueToFind == array[i]) { + return i; + } + } + return INDEX_NOT_FOUND; + } + + /** + *

Checks if the value is in the given array.

+ * + *

The method returns {@code false} if a {@code null} array is passed in.

+ * + * @param array the array to search through + * @param valueToFind the value to find + * @return {@code true} if the array contains the object + */ + public static boolean contains(int[] array, int valueToFind) { + return indexOf(array, valueToFind) != INDEX_NOT_FOUND; + } + + // short IndexOf + //----------------------------------------------------------------------- + /** + *

Finds the index of the given value in the array.

+ * + *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.

+ * + * @param array the array to search through for the object, may be {@code null} + * @param valueToFind the value to find + * @return the index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int indexOf(short[] array, short valueToFind) { + return indexOf(array, valueToFind, 0); + } + + /** + *

Finds the index of the given value in the array starting at the given index.

+ * + *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.

+ * + *

A negative startIndex is treated as zero. A startIndex larger than the array + * length will return {@link #INDEX_NOT_FOUND} ({@code -1}).

+ * + * @param array the array to search through for the object, may be {@code null} + * @param valueToFind the value to find + * @param startIndex the index to start searching at + * @return the index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int indexOf(short[] array, short valueToFind, int startIndex) { + if (array == null) { + return INDEX_NOT_FOUND; + } + if (startIndex < 0) { + startIndex = 0; + } + for (int i = startIndex; i < array.length; i++) { + if (valueToFind == array[i]) { + return i; + } + } + return INDEX_NOT_FOUND; + } + + /** + *

Finds the last index of the given value within the array.

+ * + *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.

+ * + * @param array the array to travers backwords looking for the object, may be {@code null} + * @param valueToFind the object to find + * @return the last index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int lastIndexOf(short[] array, short valueToFind) { + return lastIndexOf(array, valueToFind, Integer.MAX_VALUE); + } + + /** + *

Finds the last index of the given value in the array starting at the given index.

+ * + *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.

+ * + *

A negative startIndex will return {@link #INDEX_NOT_FOUND} ({@code -1}). A startIndex larger than the + * array length will search from the end of the array.

+ * + * @param array the array to traverse for looking for the object, may be {@code null} + * @param valueToFind the value to find + * @param startIndex the start index to travers backwards from + * @return the last index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int lastIndexOf(short[] array, short valueToFind, int startIndex) { + if (array == null) { + return INDEX_NOT_FOUND; + } + if (startIndex < 0) { + return INDEX_NOT_FOUND; + } else if (startIndex >= array.length) { + startIndex = array.length - 1; + } + for (int i = startIndex; i >= 0; i--) { + if (valueToFind == array[i]) { + return i; + } + } + return INDEX_NOT_FOUND; + } + + /** + *

Checks if the value is in the given array.

+ * + *

The method returns {@code false} if a {@code null} array is passed in.

+ * + * @param array the array to search through + * @param valueToFind the value to find + * @return {@code true} if the array contains the object + */ + public static boolean contains(short[] array, short valueToFind) { + return indexOf(array, valueToFind) != INDEX_NOT_FOUND; + } + + // char IndexOf + //----------------------------------------------------------------------- + /** + *

Finds the index of the given value in the array.

+ * + *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.

+ * + * @param array the array to search through for the object, may be {@code null} + * @param valueToFind the value to find + * @return the index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + * @since 2.1 + */ + public static int indexOf(char[] array, char valueToFind) { + return indexOf(array, valueToFind, 0); + } + + /** + *

Finds the index of the given value in the array starting at the given index.

+ * + *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.

+ * + *

A negative startIndex is treated as zero. A startIndex larger than the array + * length will return {@link #INDEX_NOT_FOUND} ({@code -1}).

+ * + * @param array the array to search through for the object, may be {@code null} + * @param valueToFind the value to find + * @param startIndex the index to start searching at + * @return the index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + * @since 2.1 + */ + public static int indexOf(char[] array, char valueToFind, int startIndex) { + if (array == null) { + return INDEX_NOT_FOUND; + } + if (startIndex < 0) { + startIndex = 0; + } + for (int i = startIndex; i < array.length; i++) { + if (valueToFind == array[i]) { + return i; + } + } + return INDEX_NOT_FOUND; + } + + /** + *

Finds the last index of the given value within the array.

+ * + *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.

+ * + * @param array the array to travers backwords looking for the object, may be {@code null} + * @param valueToFind the object to find + * @return the last index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + * @since 2.1 + */ + public static int lastIndexOf(char[] array, char valueToFind) { + return lastIndexOf(array, valueToFind, Integer.MAX_VALUE); + } + + /** + *

Finds the last index of the given value in the array starting at the given index.

+ * + *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.

+ * + *

A negative startIndex will return {@link #INDEX_NOT_FOUND} ({@code -1}). A startIndex larger than the + * array length will search from the end of the array.

+ * + * @param array the array to traverse for looking for the object, may be {@code null} + * @param valueToFind the value to find + * @param startIndex the start index to travers backwards from + * @return the last index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + * @since 2.1 + */ + public static int lastIndexOf(char[] array, char valueToFind, int startIndex) { + if (array == null) { + return INDEX_NOT_FOUND; + } + if (startIndex < 0) { + return INDEX_NOT_FOUND; + } else if (startIndex >= array.length) { + startIndex = array.length - 1; + } + for (int i = startIndex; i >= 0; i--) { + if (valueToFind == array[i]) { + return i; + } + } + return INDEX_NOT_FOUND; + } + + /** + *

Checks if the value is in the given array.

+ * + *

The method returns {@code false} if a {@code null} array is passed in.

+ * + * @param array the array to search through + * @param valueToFind the value to find + * @return {@code true} if the array contains the object + * @since 2.1 + */ + public static boolean contains(char[] array, char valueToFind) { + return indexOf(array, valueToFind) != INDEX_NOT_FOUND; + } + + // byte IndexOf + //----------------------------------------------------------------------- + /** + *

Finds the index of the given value in the array.

+ * + *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.

+ * + * @param array the array to search through for the object, may be {@code null} + * @param valueToFind the value to find + * @return the index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int indexOf(byte[] array, byte valueToFind) { + return indexOf(array, valueToFind, 0); + } + + /** + *

Finds the index of the given value in the array starting at the given index.

+ * + *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.

+ * + *

A negative startIndex is treated as zero. A startIndex larger than the array + * length will return {@link #INDEX_NOT_FOUND} ({@code -1}).

+ * + * @param array the array to search through for the object, may be {@code null} + * @param valueToFind the value to find + * @param startIndex the index to start searching at + * @return the index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int indexOf(byte[] array, byte valueToFind, int startIndex) { + if (array == null) { + return INDEX_NOT_FOUND; + } + if (startIndex < 0) { + startIndex = 0; + } + for (int i = startIndex; i < array.length; i++) { + if (valueToFind == array[i]) { + return i; + } + } + return INDEX_NOT_FOUND; + } + + /** + *

Finds the last index of the given value within the array.

+ * + *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.

+ * + * @param array the array to travers backwords looking for the object, may be {@code null} + * @param valueToFind the object to find + * @return the last index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int lastIndexOf(byte[] array, byte valueToFind) { + return lastIndexOf(array, valueToFind, Integer.MAX_VALUE); + } + + /** + *

Finds the last index of the given value in the array starting at the given index.

+ * + *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.

+ * + *

A negative startIndex will return {@link #INDEX_NOT_FOUND} ({@code -1}). A startIndex larger than the + * array length will search from the end of the array.

+ * + * @param array the array to traverse for looking for the object, may be {@code null} + * @param valueToFind the value to find + * @param startIndex the start index to travers backwards from + * @return the last index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int lastIndexOf(byte[] array, byte valueToFind, int startIndex) { + if (array == null) { + return INDEX_NOT_FOUND; + } + if (startIndex < 0) { + return INDEX_NOT_FOUND; + } else if (startIndex >= array.length) { + startIndex = array.length - 1; + } + for (int i = startIndex; i >= 0; i--) { + if (valueToFind == array[i]) { + return i; + } + } + return INDEX_NOT_FOUND; + } + + /** + *

Checks if the value is in the given array.

+ * + *

The method returns {@code false} if a {@code null} array is passed in.

+ * + * @param array the array to search through + * @param valueToFind the value to find + * @return {@code true} if the array contains the object + */ + public static boolean contains(byte[] array, byte valueToFind) { + return indexOf(array, valueToFind) != INDEX_NOT_FOUND; + } + + // double IndexOf + //----------------------------------------------------------------------- + /** + *

Finds the index of the given value in the array.

+ * + *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.

+ * + * @param array the array to search through for the object, may be {@code null} + * @param valueToFind the value to find + * @return the index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int indexOf(double[] array, double valueToFind) { + return indexOf(array, valueToFind, 0); + } + + /** + *

Finds the index of the given value within a given tolerance in the array. + * This method will return the index of the first value which falls between the region + * defined by valueToFind - tolerance and valueToFind + tolerance.

+ * + *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.

+ * + * @param array the array to search through for the object, may be {@code null} + * @param valueToFind the value to find + * @param tolerance tolerance of the search + * @return the index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int indexOf(double[] array, double valueToFind, double tolerance) { + return indexOf(array, valueToFind, 0, tolerance); + } + + /** + *

Finds the index of the given value in the array starting at the given index.

+ * + *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.

+ * + *

A negative startIndex is treated as zero. A startIndex larger than the array + * length will return {@link #INDEX_NOT_FOUND} ({@code -1}).

+ * + * @param array the array to search through for the object, may be {@code null} + * @param valueToFind the value to find + * @param startIndex the index to start searching at + * @return the index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int indexOf(double[] array, double valueToFind, int startIndex) { + if (ArrayUtils.isEmpty(array)) { + return INDEX_NOT_FOUND; + } + if (startIndex < 0) { + startIndex = 0; + } + for (int i = startIndex; i < array.length; i++) { + if (valueToFind == array[i]) { + return i; + } + } + return INDEX_NOT_FOUND; + } + + /** + *

Finds the index of the given value in the array starting at the given index. + * This method will return the index of the first value which falls between the region + * defined by valueToFind - tolerance and valueToFind + tolerance.

+ * + *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.

+ * + *

A negative startIndex is treated as zero. A startIndex larger than the array + * length will return {@link #INDEX_NOT_FOUND} ({@code -1}).

+ * + * @param array the array to search through for the object, may be {@code null} + * @param valueToFind the value to find + * @param startIndex the index to start searching at + * @param tolerance tolerance of the search + * @return the index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int indexOf(double[] array, double valueToFind, int startIndex, double tolerance) { + if (ArrayUtils.isEmpty(array)) { + return INDEX_NOT_FOUND; + } + if (startIndex < 0) { + startIndex = 0; + } + double min = valueToFind - tolerance; + double max = valueToFind + tolerance; + for (int i = startIndex; i < array.length; i++) { + if (array[i] >= min && array[i] <= max) { + return i; + } + } + return INDEX_NOT_FOUND; + } + + /** + *

Finds the last index of the given value within the array.

+ * + *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.

+ * + * @param array the array to travers backwords looking for the object, may be {@code null} + * @param valueToFind the object to find + * @return the last index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int lastIndexOf(double[] array, double valueToFind) { + return lastIndexOf(array, valueToFind, Integer.MAX_VALUE); + } + + /** + *

Finds the last index of the given value within a given tolerance in the array. + * This method will return the index of the last value which falls between the region + * defined by valueToFind - tolerance and valueToFind + tolerance.

+ * + *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.

+ * + * @param array the array to search through for the object, may be {@code null} + * @param valueToFind the value to find + * @param tolerance tolerance of the search + * @return the index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int lastIndexOf(double[] array, double valueToFind, double tolerance) { + return lastIndexOf(array, valueToFind, Integer.MAX_VALUE, tolerance); + } + + /** + *

Finds the last index of the given value in the array starting at the given index.

+ * + *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.

+ * + *

A negative startIndex will return {@link #INDEX_NOT_FOUND} ({@code -1}). A startIndex larger than the + * array length will search from the end of the array.

+ * + * @param array the array to traverse for looking for the object, may be {@code null} + * @param valueToFind the value to find + * @param startIndex the start index to travers backwards from + * @return the last index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int lastIndexOf(double[] array, double valueToFind, int startIndex) { + if (ArrayUtils.isEmpty(array)) { + return INDEX_NOT_FOUND; + } + if (startIndex < 0) { + return INDEX_NOT_FOUND; + } else if (startIndex >= array.length) { + startIndex = array.length - 1; + } + for (int i = startIndex; i >= 0; i--) { + if (valueToFind == array[i]) { + return i; + } + } + return INDEX_NOT_FOUND; + } + + /** + *

Finds the last index of the given value in the array starting at the given index. + * This method will return the index of the last value which falls between the region + * defined by valueToFind - tolerance and valueToFind + tolerance.

+ * + *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.

+ * + *

A negative startIndex will return {@link #INDEX_NOT_FOUND} ({@code -1}). A startIndex larger than the + * array length will search from the end of the array.

+ * + * @param array the array to traverse for looking for the object, may be {@code null} + * @param valueToFind the value to find + * @param startIndex the start index to travers backwards from + * @param tolerance search for value within plus/minus this amount + * @return the last index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int lastIndexOf(double[] array, double valueToFind, int startIndex, double tolerance) { + if (ArrayUtils.isEmpty(array)) { + return INDEX_NOT_FOUND; + } + if (startIndex < 0) { + return INDEX_NOT_FOUND; + } else if (startIndex >= array.length) { + startIndex = array.length - 1; + } + double min = valueToFind - tolerance; + double max = valueToFind + tolerance; + for (int i = startIndex; i >= 0; i--) { + if (array[i] >= min && array[i] <= max) { + return i; + } + } + return INDEX_NOT_FOUND; + } + + /** + *

Checks if the value is in the given array.

+ * + *

The method returns {@code false} if a {@code null} array is passed in.

+ * + * @param array the array to search through + * @param valueToFind the value to find + * @return {@code true} if the array contains the object + */ + public static boolean contains(double[] array, double valueToFind) { + return indexOf(array, valueToFind) != INDEX_NOT_FOUND; + } + + /** + *

Checks if a value falling within the given tolerance is in the + * given array. If the array contains a value within the inclusive range + * defined by (value - tolerance) to (value + tolerance).

+ * + *

The method returns {@code false} if a {@code null} array + * is passed in.

+ * + * @param array the array to search + * @param valueToFind the value to find + * @param tolerance the array contains the tolerance of the search + * @return true if value falling within tolerance is in array + */ + public static boolean contains(double[] array, double valueToFind, double tolerance) { + return indexOf(array, valueToFind, 0, tolerance) != INDEX_NOT_FOUND; + } + + // float IndexOf + //----------------------------------------------------------------------- + /** + *

Finds the index of the given value in the array.

+ * + *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.

+ * + * @param array the array to search through for the object, may be {@code null} + * @param valueToFind the value to find + * @return the index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int indexOf(float[] array, float valueToFind) { + return indexOf(array, valueToFind, 0); + } + + /** + *

Finds the index of the given value in the array starting at the given index.

+ * + *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.

+ * + *

A negative startIndex is treated as zero. A startIndex larger than the array + * length will return {@link #INDEX_NOT_FOUND} ({@code -1}).

+ * + * @param array the array to search through for the object, may be {@code null} + * @param valueToFind the value to find + * @param startIndex the index to start searching at + * @return the index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int indexOf(float[] array, float valueToFind, int startIndex) { + if (ArrayUtils.isEmpty(array)) { + return INDEX_NOT_FOUND; + } + if (startIndex < 0) { + startIndex = 0; + } + for (int i = startIndex; i < array.length; i++) { + if (valueToFind == array[i]) { + return i; + } + } + return INDEX_NOT_FOUND; + } + + /** + *

Finds the last index of the given value within the array.

+ * + *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.

+ * + * @param array the array to travers backwords looking for the object, may be {@code null} + * @param valueToFind the object to find + * @return the last index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int lastIndexOf(float[] array, float valueToFind) { + return lastIndexOf(array, valueToFind, Integer.MAX_VALUE); + } + + /** + *

Finds the last index of the given value in the array starting at the given index.

+ * + *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.

+ * + *

A negative startIndex will return {@link #INDEX_NOT_FOUND} ({@code -1}). A startIndex larger than the + * array length will search from the end of the array.

+ * + * @param array the array to traverse for looking for the object, may be {@code null} + * @param valueToFind the value to find + * @param startIndex the start index to travers backwards from + * @return the last index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int lastIndexOf(float[] array, float valueToFind, int startIndex) { + if (ArrayUtils.isEmpty(array)) { + return INDEX_NOT_FOUND; + } + if (startIndex < 0) { + return INDEX_NOT_FOUND; + } else if (startIndex >= array.length) { + startIndex = array.length - 1; + } + for (int i = startIndex; i >= 0; i--) { + if (valueToFind == array[i]) { + return i; + } + } + return INDEX_NOT_FOUND; + } + + /** + *

Checks if the value is in the given array.

+ * + *

The method returns {@code false} if a {@code null} array is passed in.

+ * + * @param array the array to search through + * @param valueToFind the value to find + * @return {@code true} if the array contains the object + */ + public static boolean contains(float[] array, float valueToFind) { + return indexOf(array, valueToFind) != INDEX_NOT_FOUND; + } + + // boolean IndexOf + //----------------------------------------------------------------------- + /** + *

Finds the index of the given value in the array.

+ * + *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.

+ * + * @param array the array to search through for the object, may be {@code null} + * @param valueToFind the value to find + * @return the index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int indexOf(boolean[] array, boolean valueToFind) { + return indexOf(array, valueToFind, 0); + } + + /** + *

Finds the index of the given value in the array starting at the given index.

+ * + *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.

+ * + *

A negative startIndex is treated as zero. A startIndex larger than the array + * length will return {@link #INDEX_NOT_FOUND} ({@code -1}).

+ * + * @param array the array to search through for the object, may be {@code null} + * @param valueToFind the value to find + * @param startIndex the index to start searching at + * @return the index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} + * array input + */ + public static int indexOf(boolean[] array, boolean valueToFind, int startIndex) { + if (ArrayUtils.isEmpty(array)) { + return INDEX_NOT_FOUND; + } + if (startIndex < 0) { + startIndex = 0; + } + for (int i = startIndex; i < array.length; i++) { + if (valueToFind == array[i]) { + return i; + } + } + return INDEX_NOT_FOUND; + } + + /** + *

Finds the last index of the given value within the array.

+ * + *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) if + * {@code null} array input.

+ * + * @param array the array to travers backwords looking for the object, may be {@code null} + * @param valueToFind the object to find + * @return the last index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int lastIndexOf(boolean[] array, boolean valueToFind) { + return lastIndexOf(array, valueToFind, Integer.MAX_VALUE); + } + + /** + *

Finds the last index of the given value in the array starting at the given index.

+ * + *

This method returns {@link #INDEX_NOT_FOUND} ({@code -1}) for a {@code null} input array.

+ * + *

A negative startIndex will return {@link #INDEX_NOT_FOUND} ({@code -1}). A startIndex larger than + * the array length will search from the end of the array.

+ * + * @param array the array to traverse for looking for the object, may be {@code null} + * @param valueToFind the value to find + * @param startIndex the start index to travers backwards from + * @return the last index of the value within the array, + * {@link #INDEX_NOT_FOUND} ({@code -1}) if not found or {@code null} array input + */ + public static int lastIndexOf(boolean[] array, boolean valueToFind, int startIndex) { + if (ArrayUtils.isEmpty(array)) { + return INDEX_NOT_FOUND; + } + if (startIndex < 0) { + return INDEX_NOT_FOUND; + } else if (startIndex >= array.length) { + startIndex = array.length - 1; + } + for (int i = startIndex; i >= 0; i--) { + if (valueToFind == array[i]) { + return i; + } + } + return INDEX_NOT_FOUND; + } + + /** + *

Checks if the value is in the given array.

+ * + *

The method returns {@code false} if a {@code null} array is passed in.

+ * + * @param array the array to search through + * @param valueToFind the value to find + * @return {@code true} if the array contains the object + */ + public static boolean contains(boolean[] array, boolean valueToFind) { + return indexOf(array, valueToFind) != INDEX_NOT_FOUND; + } + + // Primitive/Object array converters + // ---------------------------------------------------------------------- + + // Character array converters + // ---------------------------------------------------------------------- + /** + *

Converts an array of object Characters to primitives.

+ * + *

This method returns {@code null} for a {@code null} input array.

+ * + * @param array a {@code Character} array, may be {@code null} + * @return a {@code char} array, {@code null} if null array input + * @throws NullPointerException if array content is {@code null} + */ + public static char[] toPrimitive(Character[] array) { + if (array == null) { + return null; + } else if (array.length == 0) { + return EMPTY_CHAR_ARRAY; + } + final char[] result = new char[array.length]; + for (int i = 0; i < array.length; i++) { + result[i] = array[i].charValue(); + } + return result; + } + + /** + *

Converts an array of object Character to primitives handling {@code null}.

+ * + *

This method returns {@code null} for a {@code null} input array.

+ * + * @param array a {@code Character} array, may be {@code null} + * @param valueForNull the value to insert if {@code null} found + * @return a {@code char} array, {@code null} if null array input + */ + public static char[] toPrimitive(Character[] array, char valueForNull) { + if (array == null) { + return null; + } else if (array.length == 0) { + return EMPTY_CHAR_ARRAY; + } + final char[] result = new char[array.length]; + for (int i = 0; i < array.length; i++) { + Character b = array[i]; + result[i] = (b == null ? valueForNull : b.charValue()); + } + return result; + } + + /** + *

Converts an array of primitive chars to objects.

+ * + *

This method returns {@code null} for a {@code null} input array.

+ * + * @param array a {@code char} array + * @return a {@code Character} array, {@code null} if null array input + */ + public static Character[] toObject(char[] array) { + if (array == null) { + return null; + } else if (array.length == 0) { + return EMPTY_CHARACTER_OBJECT_ARRAY; + } + final Character[] result = new Character[array.length]; + for (int i = 0; i < array.length; i++) { + result[i] = Character.valueOf(array[i]); + } + return result; + } + + // Long array converters + // ---------------------------------------------------------------------- + /** + *

Converts an array of object Longs to primitives.

+ * + *

This method returns {@code null} for a {@code null} input array.

+ * + * @param array a {@code Long} array, may be {@code null} + * @return a {@code long} array, {@code null} if null array input + * @throws NullPointerException if array content is {@code null} + */ + public static long[] toPrimitive(Long[] array) { + if (array == null) { + return null; + } else if (array.length == 0) { + return EMPTY_LONG_ARRAY; + } + final long[] result = new long[array.length]; + for (int i = 0; i < array.length; i++) { + result[i] = array[i].longValue(); + } + return result; + } + + /** + *

Converts an array of object Long to primitives handling {@code null}.

+ * + *

This method returns {@code null} for a {@code null} input array.

+ * + * @param array a {@code Long} array, may be {@code null} + * @param valueForNull the value to insert if {@code null} found + * @return a {@code long} array, {@code null} if null array input + */ + public static long[] toPrimitive(Long[] array, long valueForNull) { + if (array == null) { + return null; + } else if (array.length == 0) { + return EMPTY_LONG_ARRAY; + } + final long[] result = new long[array.length]; + for (int i = 0; i < array.length; i++) { + Long b = array[i]; + result[i] = (b == null ? valueForNull : b.longValue()); + } + return result; + } + + /** + *

Converts an array of primitive longs to objects.

+ * + *

This method returns {@code null} for a {@code null} input array.

+ * + * @param array a {@code long} array + * @return a {@code Long} array, {@code null} if null array input + */ + public static Long[] toObject(long[] array) { + if (array == null) { + return null; + } else if (array.length == 0) { + return EMPTY_LONG_OBJECT_ARRAY; + } + final Long[] result = new Long[array.length]; + for (int i = 0; i < array.length; i++) { + result[i] = Long.valueOf(array[i]); + } + return result; + } + + // Int array converters + // ---------------------------------------------------------------------- + /** + *

Converts an array of object Integers to primitives.

+ * + *

This method returns {@code null} for a {@code null} input array.

+ * + * @param array a {@code Integer} array, may be {@code null} + * @return an {@code int} array, {@code null} if null array input + * @throws NullPointerException if array content is {@code null} + */ + public static int[] toPrimitive(Integer[] array) { + if (array == null) { + return null; + } else if (array.length == 0) { + return EMPTY_INT_ARRAY; + } + final int[] result = new int[array.length]; + for (int i = 0; i < array.length; i++) { + result[i] = array[i].intValue(); + } + return result; + } + + /** + *

Converts an array of object Integer to primitives handling {@code null}.

+ * + *

This method returns {@code null} for a {@code null} input array.

+ * + * @param array a {@code Integer} array, may be {@code null} + * @param valueForNull the value to insert if {@code null} found + * @return an {@code int} array, {@code null} if null array input + */ + public static int[] toPrimitive(Integer[] array, int valueForNull) { + if (array == null) { + return null; + } else if (array.length == 0) { + return EMPTY_INT_ARRAY; + } + final int[] result = new int[array.length]; + for (int i = 0; i < array.length; i++) { + Integer b = array[i]; + result[i] = (b == null ? valueForNull : b.intValue()); + } + return result; + } + + /** + *

Converts an array of primitive ints to objects.

+ * + *

This method returns {@code null} for a {@code null} input array.

+ * + * @param array an {@code int} array + * @return an {@code Integer} array, {@code null} if null array input + */ + public static Integer[] toObject(int[] array) { + if (array == null) { + return null; + } else if (array.length == 0) { + return EMPTY_INTEGER_OBJECT_ARRAY; + } + final Integer[] result = new Integer[array.length]; + for (int i = 0; i < array.length; i++) { + result[i] = Integer.valueOf(array[i]); + } + return result; + } + + // Short array converters + // ---------------------------------------------------------------------- + /** + *

Converts an array of object Shorts to primitives.

+ * + *

This method returns {@code null} for a {@code null} input array.

+ * + * @param array a {@code Short} array, may be {@code null} + * @return a {@code byte} array, {@code null} if null array input + * @throws NullPointerException if array content is {@code null} + */ + public static short[] toPrimitive(Short[] array) { + if (array == null) { + return null; + } else if (array.length == 0) { + return EMPTY_SHORT_ARRAY; + } + final short[] result = new short[array.length]; + for (int i = 0; i < array.length; i++) { + result[i] = array[i].shortValue(); + } + return result; + } + + /** + *

Converts an array of object Short to primitives handling {@code null}.

+ * + *

This method returns {@code null} for a {@code null} input array.

+ * + * @param array a {@code Short} array, may be {@code null} + * @param valueForNull the value to insert if {@code null} found + * @return a {@code byte} array, {@code null} if null array input + */ + public static short[] toPrimitive(Short[] array, short valueForNull) { + if (array == null) { + return null; + } else if (array.length == 0) { + return EMPTY_SHORT_ARRAY; + } + final short[] result = new short[array.length]; + for (int i = 0; i < array.length; i++) { + Short b = array[i]; + result[i] = (b == null ? valueForNull : b.shortValue()); + } + return result; + } + + /** + *

Converts an array of primitive shorts to objects.

+ * + *

This method returns {@code null} for a {@code null} input array.

+ * + * @param array a {@code short} array + * @return a {@code Short} array, {@code null} if null array input + */ + public static Short[] toObject(short[] array) { + if (array == null) { + return null; + } else if (array.length == 0) { + return EMPTY_SHORT_OBJECT_ARRAY; + } + final Short[] result = new Short[array.length]; + for (int i = 0; i < array.length; i++) { + result[i] = Short.valueOf(array[i]); + } + return result; + } + + // Byte array converters + // ---------------------------------------------------------------------- + /** + *

Converts an array of object Bytes to primitives.

+ * + *

This method returns {@code null} for a {@code null} input array.

+ * + * @param array a {@code Byte} array, may be {@code null} + * @return a {@code byte} array, {@code null} if null array input + * @throws NullPointerException if array content is {@code null} + */ + public static byte[] toPrimitive(Byte[] array) { + if (array == null) { + return null; + } else if (array.length == 0) { + return EMPTY_BYTE_ARRAY; + } + final byte[] result = new byte[array.length]; + for (int i = 0; i < array.length; i++) { + result[i] = array[i].byteValue(); + } + return result; + } + + /** + *

Converts an array of object Bytes to primitives handling {@code null}.

+ * + *

This method returns {@code null} for a {@code null} input array.

+ * + * @param array a {@code Byte} array, may be {@code null} + * @param valueForNull the value to insert if {@code null} found + * @return a {@code byte} array, {@code null} if null array input + */ + public static byte[] toPrimitive(Byte[] array, byte valueForNull) { + if (array == null) { + return null; + } else if (array.length == 0) { + return EMPTY_BYTE_ARRAY; + } + final byte[] result = new byte[array.length]; + for (int i = 0; i < array.length; i++) { + Byte b = array[i]; + result[i] = (b == null ? valueForNull : b.byteValue()); + } + return result; + } + + /** + *

Converts an array of primitive bytes to objects.

+ * + *

This method returns {@code null} for a {@code null} input array.

+ * + * @param array a {@code byte} array + * @return a {@code Byte} array, {@code null} if null array input + */ + public static Byte[] toObject(byte[] array) { + if (array == null) { + return null; + } else if (array.length == 0) { + return EMPTY_BYTE_OBJECT_ARRAY; + } + final Byte[] result = new Byte[array.length]; + for (int i = 0; i < array.length; i++) { + result[i] = Byte.valueOf(array[i]); + } + return result; + } + + // Double array converters + // ---------------------------------------------------------------------- + /** + *

Converts an array of object Doubles to primitives.

+ * + *

This method returns {@code null} for a {@code null} input array.

+ * + * @param array a {@code Double} array, may be {@code null} + * @return a {@code double} array, {@code null} if null array input + * @throws NullPointerException if array content is {@code null} + */ + public static double[] toPrimitive(Double[] array) { + if (array == null) { + return null; + } else if (array.length == 0) { + return EMPTY_DOUBLE_ARRAY; + } + final double[] result = new double[array.length]; + for (int i = 0; i < array.length; i++) { + result[i] = array[i].doubleValue(); + } + return result; + } + + /** + *

Converts an array of object Doubles to primitives handling {@code null}.

+ * + *

This method returns {@code null} for a {@code null} input array.

+ * + * @param array a {@code Double} array, may be {@code null} + * @param valueForNull the value to insert if {@code null} found + * @return a {@code double} array, {@code null} if null array input + */ + public static double[] toPrimitive(Double[] array, double valueForNull) { + if (array == null) { + return null; + } else if (array.length == 0) { + return EMPTY_DOUBLE_ARRAY; + } + final double[] result = new double[array.length]; + for (int i = 0; i < array.length; i++) { + Double b = array[i]; + result[i] = (b == null ? valueForNull : b.doubleValue()); + } + return result; + } + + /** + *

Converts an array of primitive doubles to objects.

+ * + *

This method returns {@code null} for a {@code null} input array.

+ * + * @param array a {@code double} array + * @return a {@code Double} array, {@code null} if null array input + */ + public static Double[] toObject(double[] array) { + if (array == null) { + return null; + } else if (array.length == 0) { + return EMPTY_DOUBLE_OBJECT_ARRAY; + } + final Double[] result = new Double[array.length]; + for (int i = 0; i < array.length; i++) { + result[i] = Double.valueOf(array[i]); + } + return result; + } + + // Float array converters + // ---------------------------------------------------------------------- + /** + *

Converts an array of object Floats to primitives.

+ * + *

This method returns {@code null} for a {@code null} input array.

+ * + * @param array a {@code Float} array, may be {@code null} + * @return a {@code float} array, {@code null} if null array input + * @throws NullPointerException if array content is {@code null} + */ + public static float[] toPrimitive(Float[] array) { + if (array == null) { + return null; + } else if (array.length == 0) { + return EMPTY_FLOAT_ARRAY; + } + final float[] result = new float[array.length]; + for (int i = 0; i < array.length; i++) { + result[i] = array[i].floatValue(); + } + return result; + } + + /** + *

Converts an array of object Floats to primitives handling {@code null}.

+ * + *

This method returns {@code null} for a {@code null} input array.

+ * + * @param array a {@code Float} array, may be {@code null} + * @param valueForNull the value to insert if {@code null} found + * @return a {@code float} array, {@code null} if null array input + */ + public static float[] toPrimitive(Float[] array, float valueForNull) { + if (array == null) { + return null; + } else if (array.length == 0) { + return EMPTY_FLOAT_ARRAY; + } + final float[] result = new float[array.length]; + for (int i = 0; i < array.length; i++) { + Float b = array[i]; + result[i] = (b == null ? valueForNull : b.floatValue()); + } + return result; + } + + /** + *

Converts an array of primitive floats to objects.

+ * + *

This method returns {@code null} for a {@code null} input array.

+ * + * @param array a {@code float} array + * @return a {@code Float} array, {@code null} if null array input + */ + public static Float[] toObject(float[] array) { + if (array == null) { + return null; + } else if (array.length == 0) { + return EMPTY_FLOAT_OBJECT_ARRAY; + } + final Float[] result = new Float[array.length]; + for (int i = 0; i < array.length; i++) { + result[i] = Float.valueOf(array[i]); + } + return result; + } + + // Boolean array converters + // ---------------------------------------------------------------------- + /** + *

Converts an array of object Booleans to primitives.

+ * + *

This method returns {@code null} for a {@code null} input array.

+ * + * @param array a {@code Boolean} array, may be {@code null} + * @return a {@code boolean} array, {@code null} if null array input + * @throws NullPointerException if array content is {@code null} + */ + public static boolean[] toPrimitive(Boolean[] array) { + if (array == null) { + return null; + } else if (array.length == 0) { + return EMPTY_BOOLEAN_ARRAY; + } + final boolean[] result = new boolean[array.length]; + for (int i = 0; i < array.length; i++) { + result[i] = array[i].booleanValue(); + } + return result; + } + + /** + *

Converts an array of object Booleans to primitives handling {@code null}.

+ * + *

This method returns {@code null} for a {@code null} input array.

+ * + * @param array a {@code Boolean} array, may be {@code null} + * @param valueForNull the value to insert if {@code null} found + * @return a {@code boolean} array, {@code null} if null array input + */ + public static boolean[] toPrimitive(Boolean[] array, boolean valueForNull) { + if (array == null) { + return null; + } else if (array.length == 0) { + return EMPTY_BOOLEAN_ARRAY; + } + final boolean[] result = new boolean[array.length]; + for (int i = 0; i < array.length; i++) { + Boolean b = array[i]; + result[i] = (b == null ? valueForNull : b.booleanValue()); + } + return result; + } + + /** + *

Converts an array of primitive booleans to objects.

+ * + *

This method returns {@code null} for a {@code null} input array.

+ * + * @param array a {@code boolean} array + * @return a {@code Boolean} array, {@code null} if null array input + */ + public static Boolean[] toObject(boolean[] array) { + if (array == null) { + return null; + } else if (array.length == 0) { + return EMPTY_BOOLEAN_OBJECT_ARRAY; + } + final Boolean[] result = new Boolean[array.length]; + for (int i = 0; i < array.length; i++) { + result[i] = (array[i] ? Boolean.TRUE : Boolean.FALSE); + } + return result; + } + + // ---------------------------------------------------------------------- + /** + *

Checks if an array of Objects is empty or {@code null}.

+ * + * @param array the array to test + * @return {@code true} if the array is empty or {@code null} + * @since 2.1 + */ + public static boolean isEmpty(Object[] array) { + return array == null || array.length == 0; + } + + /** + *

Checks if an array of primitive longs is empty or {@code null}.

+ * + * @param array the array to test + * @return {@code true} if the array is empty or {@code null} + * @since 2.1 + */ + public static boolean isEmpty(long[] array) { + return array == null || array.length == 0; + } + + /** + *

Checks if an array of primitive ints is empty or {@code null}.

+ * + * @param array the array to test + * @return {@code true} if the array is empty or {@code null} + * @since 2.1 + */ + public static boolean isEmpty(int[] array) { + return array == null || array.length == 0; + } + + /** + *

Checks if an array of primitive shorts is empty or {@code null}.

+ * + * @param array the array to test + * @return {@code true} if the array is empty or {@code null} + * @since 2.1 + */ + public static boolean isEmpty(short[] array) { + return array == null || array.length == 0; + } + + /** + *

Checks if an array of primitive chars is empty or {@code null}.

+ * + * @param array the array to test + * @return {@code true} if the array is empty or {@code null} + * @since 2.1 + */ + public static boolean isEmpty(char[] array) { + return array == null || array.length == 0; + } + + /** + *

Checks if an array of primitive bytes is empty or {@code null}.

+ * + * @param array the array to test + * @return {@code true} if the array is empty or {@code null} + * @since 2.1 + */ + public static boolean isEmpty(byte[] array) { + return array == null || array.length == 0; + } + + /** + *

Checks if an array of primitive doubles is empty or {@code null}.

+ * + * @param array the array to test + * @return {@code true} if the array is empty or {@code null} + * @since 2.1 + */ + public static boolean isEmpty(double[] array) { + return array == null || array.length == 0; + } + + /** + *

Checks if an array of primitive floats is empty or {@code null}.

+ * + * @param array the array to test + * @return {@code true} if the array is empty or {@code null} + * @since 2.1 + */ + public static boolean isEmpty(float[] array) { + return array == null || array.length == 0; + } + + /** + *

Checks if an array of primitive booleans is empty or {@code null}.

+ * + * @param array the array to test + * @return {@code true} if the array is empty or {@code null} + * @since 2.1 + */ + public static boolean isEmpty(boolean[] array) { + return array == null || array.length == 0; + } + + // ---------------------------------------------------------------------- + /** + *

Checks if an array of Objects is not empty or not {@code null}.

+ * + * @param the component type of the array + * @param array the array to test + * @return {@code true} if the array is not empty or not {@code null} + * @since 2.5 + */ + public static boolean isNotEmpty(T[] array) { + return (array != null && array.length != 0); + } + + /** + *

Checks if an array of primitive longs is not empty or not {@code null}.

+ * + * @param array the array to test + * @return {@code true} if the array is not empty or not {@code null} + * @since 2.5 + */ + public static boolean isNotEmpty(long[] array) { + return (array != null && array.length != 0); + } + + /** + *

Checks if an array of primitive ints is not empty or not {@code null}.

+ * + * @param array the array to test + * @return {@code true} if the array is not empty or not {@code null} + * @since 2.5 + */ + public static boolean isNotEmpty(int[] array) { + return (array != null && array.length != 0); + } + + /** + *

Checks if an array of primitive shorts is not empty or not {@code null}.

+ * + * @param array the array to test + * @return {@code true} if the array is not empty or not {@code null} + * @since 2.5 + */ + public static boolean isNotEmpty(short[] array) { + return (array != null && array.length != 0); + } + + /** + *

Checks if an array of primitive chars is not empty or not {@code null}.

+ * + * @param array the array to test + * @return {@code true} if the array is not empty or not {@code null} + * @since 2.5 + */ + public static boolean isNotEmpty(char[] array) { + return (array != null && array.length != 0); + } + + /** + *

Checks if an array of primitive bytes is not empty or not {@code null}.

+ * + * @param array the array to test + * @return {@code true} if the array is not empty or not {@code null} + * @since 2.5 + */ + public static boolean isNotEmpty(byte[] array) { + return (array != null && array.length != 0); + } + + /** + *

Checks if an array of primitive doubles is not empty or not {@code null}.

+ * + * @param array the array to test + * @return {@code true} if the array is not empty or not {@code null} + * @since 2.5 + */ + public static boolean isNotEmpty(double[] array) { + return (array != null && array.length != 0); + } + + /** + *

Checks if an array of primitive floats is not empty or not {@code null}.

+ * + * @param array the array to test + * @return {@code true} if the array is not empty or not {@code null} + * @since 2.5 + */ + public static boolean isNotEmpty(float[] array) { + return (array != null && array.length != 0); + } + + /** + *

Checks if an array of primitive booleans is not empty or not {@code null}.

+ * + * @param array the array to test + * @return {@code true} if the array is not empty or not {@code null} + * @since 2.5 + */ + public static boolean isNotEmpty(boolean[] array) { + return (array != null && array.length != 0); + } + + /** + *

Adds all the elements of the given arrays into a new array.

+ *

The new array contains all of the element of {@code array1} followed + * by all of the elements {@code array2}. When an array is returned, it is always + * a new array.

+ * + *
+     * ArrayUtils.addAll(null, null)     = null
+     * ArrayUtils.addAll(array1, null)   = cloned copy of array1
+     * ArrayUtils.addAll(null, array2)   = cloned copy of array2
+     * ArrayUtils.addAll([], [])         = []
+     * ArrayUtils.addAll([null], [null]) = [null, null]
+     * ArrayUtils.addAll(["a", "b", "c"], ["1", "2", "3"]) = ["a", "b", "c", "1", "2", "3"]
+     * 
+ * + * @param the component type of the array + * @param array1 the first array whose elements are added to the new array, may be {@code null} + * @param array2 the second array whose elements are added to the new array, may be {@code null} + * @return The new array, {@code null} if both arrays are {@code null}. + * The type of the new array is the type of the first array, + * unless the first array is null, in which case the type is the same as the second array. + * @since 2.1 + * @throws IllegalArgumentException if the array types are incompatible + */ + public static T[] addAll(T[] array1, T... array2) { + if (array1 == null) { + return clone(array2); + } else if (array2 == null) { + return clone(array1); + } + final Class type1 = array1.getClass().getComponentType(); + @SuppressWarnings("unchecked") // OK, because array is of type T + T[] joinedArray = (T[]) Array.newInstance(type1, array1.length + array2.length); + System.arraycopy(array1, 0, joinedArray, 0, array1.length); + try { + System.arraycopy(array2, 0, joinedArray, array1.length, array2.length); + } catch (ArrayStoreException ase) { + // Check if problem was due to incompatible types + /* + * We do this here, rather than before the copy because: + * - it would be a wasted check most of the time + * - safer, in case check turns out to be too strict + */ + final Class type2 = array2.getClass().getComponentType(); + if (!type1.isAssignableFrom(type2)){ + throw new IllegalArgumentException("Cannot store "+type2.getName()+" in an array of " + +type1.getName(), ase); + } + throw ase; // No, so rethrow original + } + return joinedArray; + } + + /** + *

Adds all the elements of the given arrays into a new array.

+ *

The new array contains all of the element of {@code array1} followed + * by all of the elements {@code array2}. When an array is returned, it is always + * a new array.

+ * + *
+     * ArrayUtils.addAll(array1, null)   = cloned copy of array1
+     * ArrayUtils.addAll(null, array2)   = cloned copy of array2
+     * ArrayUtils.addAll([], [])         = []
+     * 
+ * + * @param array1 the first array whose elements are added to the new array. + * @param array2 the second array whose elements are added to the new array. + * @return The new boolean[] array. + * @since 2.1 + */ + public static boolean[] addAll(boolean[] array1, boolean... array2) { + if (array1 == null) { + return clone(array2); + } else if (array2 == null) { + return clone(array1); + } + boolean[] joinedArray = new boolean[array1.length + array2.length]; + System.arraycopy(array1, 0, joinedArray, 0, array1.length); + System.arraycopy(array2, 0, joinedArray, array1.length, array2.length); + return joinedArray; + } + + /** + *

Adds all the elements of the given arrays into a new array.

+ *

The new array contains all of the element of {@code array1} followed + * by all of the elements {@code array2}. When an array is returned, it is always + * a new array.

+ * + *
+     * ArrayUtils.addAll(array1, null)   = cloned copy of array1
+     * ArrayUtils.addAll(null, array2)   = cloned copy of array2
+     * ArrayUtils.addAll([], [])         = []
+     * 
+ * + * @param array1 the first array whose elements are added to the new array. + * @param array2 the second array whose elements are added to the new array. + * @return The new char[] array. + * @since 2.1 + */ + public static char[] addAll(char[] array1, char... array2) { + if (array1 == null) { + return clone(array2); + } else if (array2 == null) { + return clone(array1); + } + char[] joinedArray = new char[array1.length + array2.length]; + System.arraycopy(array1, 0, joinedArray, 0, array1.length); + System.arraycopy(array2, 0, joinedArray, array1.length, array2.length); + return joinedArray; + } + + /** + *

Adds all the elements of the given arrays into a new array.

+ *

The new array contains all of the element of {@code array1} followed + * by all of the elements {@code array2}. When an array is returned, it is always + * a new array.

+ * + *
+     * ArrayUtils.addAll(array1, null)   = cloned copy of array1
+     * ArrayUtils.addAll(null, array2)   = cloned copy of array2
+     * ArrayUtils.addAll([], [])         = []
+     * 
+ * + * @param array1 the first array whose elements are added to the new array. + * @param array2 the second array whose elements are added to the new array. + * @return The new byte[] array. + * @since 2.1 + */ + public static byte[] addAll(byte[] array1, byte... array2) { + if (array1 == null) { + return clone(array2); + } else if (array2 == null) { + return clone(array1); + } + byte[] joinedArray = new byte[array1.length + array2.length]; + System.arraycopy(array1, 0, joinedArray, 0, array1.length); + System.arraycopy(array2, 0, joinedArray, array1.length, array2.length); + return joinedArray; + } + + /** + *

Adds all the elements of the given arrays into a new array.

+ *

The new array contains all of the element of {@code array1} followed + * by all of the elements {@code array2}. When an array is returned, it is always + * a new array.

+ * + *
+     * ArrayUtils.addAll(array1, null)   = cloned copy of array1
+     * ArrayUtils.addAll(null, array2)   = cloned copy of array2
+     * ArrayUtils.addAll([], [])         = []
+     * 
+ * + * @param array1 the first array whose elements are added to the new array. + * @param array2 the second array whose elements are added to the new array. + * @return The new short[] array. + * @since 2.1 + */ + public static short[] addAll(short[] array1, short... array2) { + if (array1 == null) { + return clone(array2); + } else if (array2 == null) { + return clone(array1); + } + short[] joinedArray = new short[array1.length + array2.length]; + System.arraycopy(array1, 0, joinedArray, 0, array1.length); + System.arraycopy(array2, 0, joinedArray, array1.length, array2.length); + return joinedArray; + } + + /** + *

Adds all the elements of the given arrays into a new array.

+ *

The new array contains all of the element of {@code array1} followed + * by all of the elements {@code array2}. When an array is returned, it is always + * a new array.

+ * + *
+     * ArrayUtils.addAll(array1, null)   = cloned copy of array1
+     * ArrayUtils.addAll(null, array2)   = cloned copy of array2
+     * ArrayUtils.addAll([], [])         = []
+     * 
+ * + * @param array1 the first array whose elements are added to the new array. + * @param array2 the second array whose elements are added to the new array. + * @return The new int[] array. + * @since 2.1 + */ + public static int[] addAll(int[] array1, int... array2) { + if (array1 == null) { + return clone(array2); + } else if (array2 == null) { + return clone(array1); + } + int[] joinedArray = new int[array1.length + array2.length]; + System.arraycopy(array1, 0, joinedArray, 0, array1.length); + System.arraycopy(array2, 0, joinedArray, array1.length, array2.length); + return joinedArray; + } + + /** + *

Adds all the elements of the given arrays into a new array.

+ *

The new array contains all of the element of {@code array1} followed + * by all of the elements {@code array2}. When an array is returned, it is always + * a new array.

+ * + *
+     * ArrayUtils.addAll(array1, null)   = cloned copy of array1
+     * ArrayUtils.addAll(null, array2)   = cloned copy of array2
+     * ArrayUtils.addAll([], [])         = []
+     * 
+ * + * @param array1 the first array whose elements are added to the new array. + * @param array2 the second array whose elements are added to the new array. + * @return The new long[] array. + * @since 2.1 + */ + public static long[] addAll(long[] array1, long... array2) { + if (array1 == null) { + return clone(array2); + } else if (array2 == null) { + return clone(array1); + } + long[] joinedArray = new long[array1.length + array2.length]; + System.arraycopy(array1, 0, joinedArray, 0, array1.length); + System.arraycopy(array2, 0, joinedArray, array1.length, array2.length); + return joinedArray; + } + + /** + *

Adds all the elements of the given arrays into a new array.

+ *

The new array contains all of the element of {@code array1} followed + * by all of the elements {@code array2}. When an array is returned, it is always + * a new array.

+ * + *
+     * ArrayUtils.addAll(array1, null)   = cloned copy of array1
+     * ArrayUtils.addAll(null, array2)   = cloned copy of array2
+     * ArrayUtils.addAll([], [])         = []
+     * 
+ * + * @param array1 the first array whose elements are added to the new array. + * @param array2 the second array whose elements are added to the new array. + * @return The new float[] array. + * @since 2.1 + */ + public static float[] addAll(float[] array1, float... array2) { + if (array1 == null) { + return clone(array2); + } else if (array2 == null) { + return clone(array1); + } + float[] joinedArray = new float[array1.length + array2.length]; + System.arraycopy(array1, 0, joinedArray, 0, array1.length); + System.arraycopy(array2, 0, joinedArray, array1.length, array2.length); + return joinedArray; + } + + /** + *

Adds all the elements of the given arrays into a new array.

+ *

The new array contains all of the element of {@code array1} followed + * by all of the elements {@code array2}. When an array is returned, it is always + * a new array.

+ * + *
+     * ArrayUtils.addAll(array1, null)   = cloned copy of array1
+     * ArrayUtils.addAll(null, array2)   = cloned copy of array2
+     * ArrayUtils.addAll([], [])         = []
+     * 
+ * + * @param array1 the first array whose elements are added to the new array. + * @param array2 the second array whose elements are added to the new array. + * @return The new double[] array. + * @since 2.1 + */ + public static double[] addAll(double[] array1, double... array2) { + if (array1 == null) { + return clone(array2); + } else if (array2 == null) { + return clone(array1); + } + double[] joinedArray = new double[array1.length + array2.length]; + System.arraycopy(array1, 0, joinedArray, 0, array1.length); + System.arraycopy(array2, 0, joinedArray, array1.length, array2.length); + return joinedArray; + } + + /** + *

Copies the given array and adds the given element at the end of the new array.

+ * + *

The new array contains the same elements of the input + * array plus the given element in the last position. The component type of + * the new array is the same as that of the input array.

+ * + *

If the input array is {@code null}, a new one element array is returned + * whose component type is the same as the element, unless the element itself is null, + * in which case the return type is Object[]

+ * + *
+     * ArrayUtils.add(null, null)      = [null]
+     * ArrayUtils.add(null, "a")       = ["a"]
+     * ArrayUtils.add(["a"], null)     = ["a", null]
+     * ArrayUtils.add(["a"], "b")      = ["a", "b"]
+     * ArrayUtils.add(["a", "b"], "c") = ["a", "b", "c"]
+     * 
+ * + * @param the component type of the array + * @param array the array to "add" the element to, may be {@code null} + * @param element the object to add, may be {@code null} + * @return A new array containing the existing elements plus the new element + * The returned array type will be that of the input array (unless null), + * in which case it will have the same type as the element. + * If both are null, an IllegalArgumentException is thrown + * @since 2.1 + * @throws IllegalArgumentException if both arguments are null + */ + public static T[] add(T[] array, T element) { + Class type; + if (array != null){ + type = array.getClass(); + } else if (element != null) { + type = element.getClass(); + } else { + throw new IllegalArgumentException("Arguments cannot both be null"); + } + @SuppressWarnings("unchecked") // type must be T + T[] newArray = (T[]) copyArrayGrow1(array, type); + newArray[newArray.length - 1] = element; + return newArray; + } + + /** + *

Copies the given array and adds the given element at the end of the new array.

+ * + *

The new array contains the same elements of the input + * array plus the given element in the last position. The component type of + * the new array is the same as that of the input array.

+ * + *

If the input array is {@code null}, a new one element array is returned + * whose component type is the same as the element.

+ * + *
+     * ArrayUtils.add(null, true)          = [true]
+     * ArrayUtils.add([true], false)       = [true, false]
+     * ArrayUtils.add([true, false], true) = [true, false, true]
+     * 
+ * + * @param array the array to copy and add the element to, may be {@code null} + * @param element the object to add at the last index of the new array + * @return A new array containing the existing elements plus the new element + * @since 2.1 + */ + public static boolean[] add(boolean[] array, boolean element) { + boolean[] newArray = (boolean[])copyArrayGrow1(array, Boolean.TYPE); + newArray[newArray.length - 1] = element; + return newArray; + } + + /** + *

Copies the given array and adds the given element at the end of the new array.

+ * + *

The new array contains the same elements of the input + * array plus the given element in the last position. The component type of + * the new array is the same as that of the input array.

+ * + *

If the input array is {@code null}, a new one element array is returned + * whose component type is the same as the element.

+ * + *
+     * ArrayUtils.add(null, 0)   = [0]
+     * ArrayUtils.add([1], 0)    = [1, 0]
+     * ArrayUtils.add([1, 0], 1) = [1, 0, 1]
+     * 
+ * + * @param array the array to copy and add the element to, may be {@code null} + * @param element the object to add at the last index of the new array + * @return A new array containing the existing elements plus the new element + * @since 2.1 + */ + public static byte[] add(byte[] array, byte element) { + byte[] newArray = (byte[])copyArrayGrow1(array, Byte.TYPE); + newArray[newArray.length - 1] = element; + return newArray; + } + + /** + *

Copies the given array and adds the given element at the end of the new array.

+ * + *

The new array contains the same elements of the input + * array plus the given element in the last position. The component type of + * the new array is the same as that of the input array.

+ * + *

If the input array is {@code null}, a new one element array is returned + * whose component type is the same as the element.

+ * + *
+     * ArrayUtils.add(null, '0')       = ['0']
+     * ArrayUtils.add(['1'], '0')      = ['1', '0']
+     * ArrayUtils.add(['1', '0'], '1') = ['1', '0', '1']
+     * 
+ * + * @param array the array to copy and add the element to, may be {@code null} + * @param element the object to add at the last index of the new array + * @return A new array containing the existing elements plus the new element + * @since 2.1 + */ + public static char[] add(char[] array, char element) { + char[] newArray = (char[])copyArrayGrow1(array, Character.TYPE); + newArray[newArray.length - 1] = element; + return newArray; + } + + /** + *

Copies the given array and adds the given element at the end of the new array.

+ * + *

The new array contains the same elements of the input + * array plus the given element in the last position. The component type of + * the new array is the same as that of the input array.

+ * + *

If the input array is {@code null}, a new one element array is returned + * whose component type is the same as the element.

+ * + *
+     * ArrayUtils.add(null, 0)   = [0]
+     * ArrayUtils.add([1], 0)    = [1, 0]
+     * ArrayUtils.add([1, 0], 1) = [1, 0, 1]
+     * 
+ * + * @param array the array to copy and add the element to, may be {@code null} + * @param element the object to add at the last index of the new array + * @return A new array containing the existing elements plus the new element + * @since 2.1 + */ + public static double[] add(double[] array, double element) { + double[] newArray = (double[])copyArrayGrow1(array, Double.TYPE); + newArray[newArray.length - 1] = element; + return newArray; + } + + /** + *

Copies the given array and adds the given element at the end of the new array.

+ * + *

The new array contains the same elements of the input + * array plus the given element in the last position. The component type of + * the new array is the same as that of the input array.

+ * + *

If the input array is {@code null}, a new one element array is returned + * whose component type is the same as the element.

+ * + *
+     * ArrayUtils.add(null, 0)   = [0]
+     * ArrayUtils.add([1], 0)    = [1, 0]
+     * ArrayUtils.add([1, 0], 1) = [1, 0, 1]
+     * 
+ * + * @param array the array to copy and add the element to, may be {@code null} + * @param element the object to add at the last index of the new array + * @return A new array containing the existing elements plus the new element + * @since 2.1 + */ + public static float[] add(float[] array, float element) { + float[] newArray = (float[])copyArrayGrow1(array, Float.TYPE); + newArray[newArray.length - 1] = element; + return newArray; + } + + /** + *

Copies the given array and adds the given element at the end of the new array.

+ * + *

The new array contains the same elements of the input + * array plus the given element in the last position. The component type of + * the new array is the same as that of the input array.

+ * + *

If the input array is {@code null}, a new one element array is returned + * whose component type is the same as the element.

+ * + *
+     * ArrayUtils.add(null, 0)   = [0]
+     * ArrayUtils.add([1], 0)    = [1, 0]
+     * ArrayUtils.add([1, 0], 1) = [1, 0, 1]
+     * 
+ * + * @param array the array to copy and add the element to, may be {@code null} + * @param element the object to add at the last index of the new array + * @return A new array containing the existing elements plus the new element + * @since 2.1 + */ + public static int[] add(int[] array, int element) { + int[] newArray = (int[])copyArrayGrow1(array, Integer.TYPE); + newArray[newArray.length - 1] = element; + return newArray; + } + + /** + *

Copies the given array and adds the given element at the end of the new array.

+ * + *

The new array contains the same elements of the input + * array plus the given element in the last position. The component type of + * the new array is the same as that of the input array.

+ * + *

If the input array is {@code null}, a new one element array is returned + * whose component type is the same as the element.

+ * + *
+     * ArrayUtils.add(null, 0)   = [0]
+     * ArrayUtils.add([1], 0)    = [1, 0]
+     * ArrayUtils.add([1, 0], 1) = [1, 0, 1]
+     * 
+ * + * @param array the array to copy and add the element to, may be {@code null} + * @param element the object to add at the last index of the new array + * @return A new array containing the existing elements plus the new element + * @since 2.1 + */ + public static long[] add(long[] array, long element) { + long[] newArray = (long[])copyArrayGrow1(array, Long.TYPE); + newArray[newArray.length - 1] = element; + return newArray; + } + + /** + *

Copies the given array and adds the given element at the end of the new array.

+ * + *

The new array contains the same elements of the input + * array plus the given element in the last position. The component type of + * the new array is the same as that of the input array.

+ * + *

If the input array is {@code null}, a new one element array is returned + * whose component type is the same as the element.

+ * + *
+     * ArrayUtils.add(null, 0)   = [0]
+     * ArrayUtils.add([1], 0)    = [1, 0]
+     * ArrayUtils.add([1, 0], 1) = [1, 0, 1]
+     * 
+ * + * @param array the array to copy and add the element to, may be {@code null} + * @param element the object to add at the last index of the new array + * @return A new array containing the existing elements plus the new element + * @since 2.1 + */ + public static short[] add(short[] array, short element) { + short[] newArray = (short[])copyArrayGrow1(array, Short.TYPE); + newArray[newArray.length - 1] = element; + return newArray; + } + + /** + * Returns a copy of the given array of size 1 greater than the argument. + * The last value of the array is left to the default value. + * + * @param array The array to copy, must not be {@code null}. + * @param newArrayComponentType If {@code array} is {@code null}, create a + * size 1 array of this type. + * @return A new copy of the array of size 1 greater than the input. + */ + private static Object copyArrayGrow1(Object array, Class newArrayComponentType) { + if (array != null) { + int arrayLength = Array.getLength(array); + Object newArray = Array.newInstance(array.getClass().getComponentType(), arrayLength + 1); + System.arraycopy(array, 0, newArray, 0, arrayLength); + return newArray; + } + return Array.newInstance(newArrayComponentType, 1); + } + + /** + *

Inserts the specified element at the specified position in the array. + * Shifts the element currently at that position (if any) and any subsequent + * elements to the right (adds one to their indices).

+ * + *

This method returns a new array with the same elements of the input + * array plus the given element on the specified position. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *

If the input array is {@code null}, a new one element array is returned + * whose component type is the same as the element.

+ * + *
+     * ArrayUtils.add(null, 0, null)      = [null]
+     * ArrayUtils.add(null, 0, "a")       = ["a"]
+     * ArrayUtils.add(["a"], 1, null)     = ["a", null]
+     * ArrayUtils.add(["a"], 1, "b")      = ["a", "b"]
+     * ArrayUtils.add(["a", "b"], 3, "c") = ["a", "b", "c"]
+     * 
+ * + * @param the component type of the array + * @param array the array to add the element to, may be {@code null} + * @param index the position of the new object + * @param element the object to add + * @return A new array containing the existing elements and the new element + * @throws IndexOutOfBoundsException if the index is out of range + * (index < 0 || index > array.length). + * @throws IllegalArgumentException if both array and element are null + */ + public static T[] add(T[] array, int index, T element) { + Class clss = null; + if (array != null) { + clss = array.getClass().getComponentType(); + } else if (element != null) { + clss = element.getClass(); + } else { + throw new IllegalArgumentException("Array and element cannot both be null"); + } + @SuppressWarnings("unchecked") // the add method creates an array of type clss, which is type T + final T[] newArray = (T[]) add(array, index, element, clss); + return newArray; + } + + /** + *

Inserts the specified element at the specified position in the array. + * Shifts the element currently at that position (if any) and any subsequent + * elements to the right (adds one to their indices).

+ * + *

This method returns a new array with the same elements of the input + * array plus the given element on the specified position. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *

If the input array is {@code null}, a new one element array is returned + * whose component type is the same as the element.

+ * + *
+     * ArrayUtils.add(null, 0, true)          = [true]
+     * ArrayUtils.add([true], 0, false)       = [false, true]
+     * ArrayUtils.add([false], 1, true)       = [false, true]
+     * ArrayUtils.add([true, false], 1, true) = [true, true, false]
+     * 
+ * + * @param array the array to add the element to, may be {@code null} + * @param index the position of the new object + * @param element the object to add + * @return A new array containing the existing elements and the new element + * @throws IndexOutOfBoundsException if the index is out of range + * (index < 0 || index > array.length). + */ + public static boolean[] add(boolean[] array, int index, boolean element) { + return (boolean[]) add(array, index, Boolean.valueOf(element), Boolean.TYPE); + } + + /** + *

Inserts the specified element at the specified position in the array. + * Shifts the element currently at that position (if any) and any subsequent + * elements to the right (adds one to their indices).

+ * + *

This method returns a new array with the same elements of the input + * array plus the given element on the specified position. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *

If the input array is {@code null}, a new one element array is returned + * whose component type is the same as the element.

+ * + *
+     * ArrayUtils.add(null, 0, 'a')            = ['a']
+     * ArrayUtils.add(['a'], 0, 'b')           = ['b', 'a']
+     * ArrayUtils.add(['a', 'b'], 0, 'c')      = ['c', 'a', 'b']
+     * ArrayUtils.add(['a', 'b'], 1, 'k')      = ['a', 'k', 'b']
+     * ArrayUtils.add(['a', 'b', 'c'], 1, 't') = ['a', 't', 'b', 'c']
+     * 
+ * + * @param array the array to add the element to, may be {@code null} + * @param index the position of the new object + * @param element the object to add + * @return A new array containing the existing elements and the new element + * @throws IndexOutOfBoundsException if the index is out of range + * (index < 0 || index > array.length). + */ + public static char[] add(char[] array, int index, char element) { + return (char[]) add(array, index, Character.valueOf(element), Character.TYPE); + } + + /** + *

Inserts the specified element at the specified position in the array. + * Shifts the element currently at that position (if any) and any subsequent + * elements to the right (adds one to their indices).

+ * + *

This method returns a new array with the same elements of the input + * array plus the given element on the specified position. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *

If the input array is {@code null}, a new one element array is returned + * whose component type is the same as the element.

+ * + *
+     * ArrayUtils.add([1], 0, 2)         = [2, 1]
+     * ArrayUtils.add([2, 6], 2, 3)      = [2, 6, 3]
+     * ArrayUtils.add([2, 6], 0, 1)      = [1, 2, 6]
+     * ArrayUtils.add([2, 6, 3], 2, 1)   = [2, 6, 1, 3]
+     * 
+ * + * @param array the array to add the element to, may be {@code null} + * @param index the position of the new object + * @param element the object to add + * @return A new array containing the existing elements and the new element + * @throws IndexOutOfBoundsException if the index is out of range + * (index < 0 || index > array.length). + */ + public static byte[] add(byte[] array, int index, byte element) { + return (byte[]) add(array, index, Byte.valueOf(element), Byte.TYPE); + } + + /** + *

Inserts the specified element at the specified position in the array. + * Shifts the element currently at that position (if any) and any subsequent + * elements to the right (adds one to their indices).

+ * + *

This method returns a new array with the same elements of the input + * array plus the given element on the specified position. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *

If the input array is {@code null}, a new one element array is returned + * whose component type is the same as the element.

+ * + *
+     * ArrayUtils.add([1], 0, 2)         = [2, 1]
+     * ArrayUtils.add([2, 6], 2, 10)     = [2, 6, 10]
+     * ArrayUtils.add([2, 6], 0, -4)     = [-4, 2, 6]
+     * ArrayUtils.add([2, 6, 3], 2, 1)   = [2, 6, 1, 3]
+     * 
+ * + * @param array the array to add the element to, may be {@code null} + * @param index the position of the new object + * @param element the object to add + * @return A new array containing the existing elements and the new element + * @throws IndexOutOfBoundsException if the index is out of range + * (index < 0 || index > array.length). + */ + public static short[] add(short[] array, int index, short element) { + return (short[]) add(array, index, Short.valueOf(element), Short.TYPE); + } + + /** + *

Inserts the specified element at the specified position in the array. + * Shifts the element currently at that position (if any) and any subsequent + * elements to the right (adds one to their indices).

+ * + *

This method returns a new array with the same elements of the input + * array plus the given element on the specified position. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *

If the input array is {@code null}, a new one element array is returned + * whose component type is the same as the element.

+ * + *
+     * ArrayUtils.add([1], 0, 2)         = [2, 1]
+     * ArrayUtils.add([2, 6], 2, 10)     = [2, 6, 10]
+     * ArrayUtils.add([2, 6], 0, -4)     = [-4, 2, 6]
+     * ArrayUtils.add([2, 6, 3], 2, 1)   = [2, 6, 1, 3]
+     * 
+ * + * @param array the array to add the element to, may be {@code null} + * @param index the position of the new object + * @param element the object to add + * @return A new array containing the existing elements and the new element + * @throws IndexOutOfBoundsException if the index is out of range + * (index < 0 || index > array.length). + */ + public static int[] add(int[] array, int index, int element) { + return (int[]) add(array, index, Integer.valueOf(element), Integer.TYPE); + } + + /** + *

Inserts the specified element at the specified position in the array. + * Shifts the element currently at that position (if any) and any subsequent + * elements to the right (adds one to their indices).

+ * + *

This method returns a new array with the same elements of the input + * array plus the given element on the specified position. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *

If the input array is {@code null}, a new one element array is returned + * whose component type is the same as the element.

+ * + *
+     * ArrayUtils.add([1L], 0, 2L)           = [2L, 1L]
+     * ArrayUtils.add([2L, 6L], 2, 10L)      = [2L, 6L, 10L]
+     * ArrayUtils.add([2L, 6L], 0, -4L)      = [-4L, 2L, 6L]
+     * ArrayUtils.add([2L, 6L, 3L], 2, 1L)   = [2L, 6L, 1L, 3L]
+     * 
+ * + * @param array the array to add the element to, may be {@code null} + * @param index the position of the new object + * @param element the object to add + * @return A new array containing the existing elements and the new element + * @throws IndexOutOfBoundsException if the index is out of range + * (index < 0 || index > array.length). + */ + public static long[] add(long[] array, int index, long element) { + return (long[]) add(array, index, Long.valueOf(element), Long.TYPE); + } + + /** + *

Inserts the specified element at the specified position in the array. + * Shifts the element currently at that position (if any) and any subsequent + * elements to the right (adds one to their indices).

+ * + *

This method returns a new array with the same elements of the input + * array plus the given element on the specified position. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *

If the input array is {@code null}, a new one element array is returned + * whose component type is the same as the element.

+ * + *
+     * ArrayUtils.add([1.1f], 0, 2.2f)               = [2.2f, 1.1f]
+     * ArrayUtils.add([2.3f, 6.4f], 2, 10.5f)        = [2.3f, 6.4f, 10.5f]
+     * ArrayUtils.add([2.6f, 6.7f], 0, -4.8f)        = [-4.8f, 2.6f, 6.7f]
+     * ArrayUtils.add([2.9f, 6.0f, 0.3f], 2, 1.0f)   = [2.9f, 6.0f, 1.0f, 0.3f]
+     * 
+ * + * @param array the array to add the element to, may be {@code null} + * @param index the position of the new object + * @param element the object to add + * @return A new array containing the existing elements and the new element + * @throws IndexOutOfBoundsException if the index is out of range + * (index < 0 || index > array.length). + */ + public static float[] add(float[] array, int index, float element) { + return (float[]) add(array, index, Float.valueOf(element), Float.TYPE); + } + + /** + *

Inserts the specified element at the specified position in the array. + * Shifts the element currently at that position (if any) and any subsequent + * elements to the right (adds one to their indices).

+ * + *

This method returns a new array with the same elements of the input + * array plus the given element on the specified position. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *

If the input array is {@code null}, a new one element array is returned + * whose component type is the same as the element.

+ * + *
+     * ArrayUtils.add([1.1], 0, 2.2)              = [2.2, 1.1]
+     * ArrayUtils.add([2.3, 6.4], 2, 10.5)        = [2.3, 6.4, 10.5]
+     * ArrayUtils.add([2.6, 6.7], 0, -4.8)        = [-4.8, 2.6, 6.7]
+     * ArrayUtils.add([2.9, 6.0, 0.3], 2, 1.0)    = [2.9, 6.0, 1.0, 0.3]
+     * 
+ * + * @param array the array to add the element to, may be {@code null} + * @param index the position of the new object + * @param element the object to add + * @return A new array containing the existing elements and the new element + * @throws IndexOutOfBoundsException if the index is out of range + * (index < 0 || index > array.length). + */ + public static double[] add(double[] array, int index, double element) { + return (double[]) add(array, index, Double.valueOf(element), Double.TYPE); + } + + /** + * Underlying implementation of add(array, index, element) methods. + * The last parameter is the class, which may not equal element.getClass + * for primitives. + * + * @param array the array to add the element to, may be {@code null} + * @param index the position of the new object + * @param element the object to add + * @param clss the type of the element being added + * @return A new array containing the existing elements and the new element + */ + private static Object add(Object array, int index, Object element, Class clss) { + if (array == null) { + if (index != 0) { + throw new IndexOutOfBoundsException("Index: " + index + ", Length: 0"); + } + Object joinedArray = Array.newInstance(clss, 1); + Array.set(joinedArray, 0, element); + return joinedArray; + } + int length = Array.getLength(array); + if (index > length || index < 0) { + throw new IndexOutOfBoundsException("Index: " + index + ", Length: " + length); + } + Object result = Array.newInstance(clss, length + 1); + System.arraycopy(array, 0, result, 0, index); + Array.set(result, index, element); + if (index < length) { + System.arraycopy(array, index, result, index + 1, length - index); + } + return result; + } + + /** + *

Removes the element at the specified position from the specified array. + * All subsequent elements are shifted to the left (subtracts one from + * their indices).

+ * + *

This method returns a new array with the same elements of the input + * array except the element on the specified position. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *

If the input array is {@code null}, an IndexOutOfBoundsException + * will be thrown, because in that case no valid index can be specified.

+ * + *
+     * ArrayUtils.remove(["a"], 0)           = []
+     * ArrayUtils.remove(["a", "b"], 0)      = ["b"]
+     * ArrayUtils.remove(["a", "b"], 1)      = ["a"]
+     * ArrayUtils.remove(["a", "b", "c"], 1) = ["a", "c"]
+     * 
+ * + * @param the component type of the array + * @param array the array to remove the element from, may not be {@code null} + * @param index the position of the element to be removed + * @return A new array containing the existing elements except the element + * at the specified position. + * @throws IndexOutOfBoundsException if the index is out of range + * (index < 0 || index >= array.length), or if the array is {@code null}. + * @since 2.1 + */ + @SuppressWarnings("unchecked") // remove() always creates an array of the same type as its input + public static T[] remove(T[] array, int index) { + return (T[]) remove((Object) array, index); + } + + /** + *

Removes the first occurrence of the specified element from the + * specified array. All subsequent elements are shifted to the left + * (subtracts one from their indices). If the array doesn't contains + * such an element, no elements are removed from the array.

+ * + *

This method returns a new array with the same elements of the input + * array except the first occurrence of the specified element. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *
+     * ArrayUtils.removeElement(null, "a")            = null
+     * ArrayUtils.removeElement([], "a")              = []
+     * ArrayUtils.removeElement(["a"], "b")           = ["a"]
+     * ArrayUtils.removeElement(["a", "b"], "a")      = ["b"]
+     * ArrayUtils.removeElement(["a", "b", "a"], "a") = ["b", "a"]
+     * 
+ * + * @param the component type of the array + * @param array the array to remove the element from, may be {@code null} + * @param element the element to be removed + * @return A new array containing the existing elements except the first + * occurrence of the specified element. + * @since 2.1 + */ + public static T[] removeElement(T[] array, Object element) { + int index = indexOf(array, element); + if (index == INDEX_NOT_FOUND) { + return clone(array); + } + return remove(array, index); + } + + /** + *

Removes the element at the specified position from the specified array. + * All subsequent elements are shifted to the left (subtracts one from + * their indices).

+ * + *

This method returns a new array with the same elements of the input + * array except the element on the specified position. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *

If the input array is {@code null}, an IndexOutOfBoundsException + * will be thrown, because in that case no valid index can be specified.

+ * + *
+     * ArrayUtils.remove([true], 0)              = []
+     * ArrayUtils.remove([true, false], 0)       = [false]
+     * ArrayUtils.remove([true, false], 1)       = [true]
+     * ArrayUtils.remove([true, true, false], 1) = [true, false]
+     * 
+ * + * @param array the array to remove the element from, may not be {@code null} + * @param index the position of the element to be removed + * @return A new array containing the existing elements except the element + * at the specified position. + * @throws IndexOutOfBoundsException if the index is out of range + * (index < 0 || index >= array.length), or if the array is {@code null}. + * @since 2.1 + */ + public static boolean[] remove(boolean[] array, int index) { + return (boolean[]) remove((Object) array, index); + } + + /** + *

Removes the first occurrence of the specified element from the + * specified array. All subsequent elements are shifted to the left + * (subtracts one from their indices). If the array doesn't contains + * such an element, no elements are removed from the array.

+ * + *

This method returns a new array with the same elements of the input + * array except the first occurrence of the specified element. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *
+     * ArrayUtils.removeElement(null, true)                = null
+     * ArrayUtils.removeElement([], true)                  = []
+     * ArrayUtils.removeElement([true], false)             = [true]
+     * ArrayUtils.removeElement([true, false], false)      = [true]
+     * ArrayUtils.removeElement([true, false, true], true) = [false, true]
+     * 
+ * + * @param array the array to remove the element from, may be {@code null} + * @param element the element to be removed + * @return A new array containing the existing elements except the first + * occurrence of the specified element. + * @since 2.1 + */ + public static boolean[] removeElement(boolean[] array, boolean element) { + int index = indexOf(array, element); + if (index == INDEX_NOT_FOUND) { + return clone(array); + } + return remove(array, index); + } + + /** + *

Removes the element at the specified position from the specified array. + * All subsequent elements are shifted to the left (subtracts one from + * their indices).

+ * + *

This method returns a new array with the same elements of the input + * array except the element on the specified position. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *

If the input array is {@code null}, an IndexOutOfBoundsException + * will be thrown, because in that case no valid index can be specified.

+ * + *
+     * ArrayUtils.remove([1], 0)          = []
+     * ArrayUtils.remove([1, 0], 0)       = [0]
+     * ArrayUtils.remove([1, 0], 1)       = [1]
+     * ArrayUtils.remove([1, 0, 1], 1)    = [1, 1]
+     * 
+ * + * @param array the array to remove the element from, may not be {@code null} + * @param index the position of the element to be removed + * @return A new array containing the existing elements except the element + * at the specified position. + * @throws IndexOutOfBoundsException if the index is out of range + * (index < 0 || index >= array.length), or if the array is {@code null}. + * @since 2.1 + */ + public static byte[] remove(byte[] array, int index) { + return (byte[]) remove((Object) array, index); + } + + /** + *

Removes the first occurrence of the specified element from the + * specified array. All subsequent elements are shifted to the left + * (subtracts one from their indices). If the array doesn't contains + * such an element, no elements are removed from the array.

+ * + *

This method returns a new array with the same elements of the input + * array except the first occurrence of the specified element. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *
+     * ArrayUtils.removeElement(null, 1)        = null
+     * ArrayUtils.removeElement([], 1)          = []
+     * ArrayUtils.removeElement([1], 0)         = [1]
+     * ArrayUtils.removeElement([1, 0], 0)      = [1]
+     * ArrayUtils.removeElement([1, 0, 1], 1)   = [0, 1]
+     * 
+ * + * @param array the array to remove the element from, may be {@code null} + * @param element the element to be removed + * @return A new array containing the existing elements except the first + * occurrence of the specified element. + * @since 2.1 + */ + public static byte[] removeElement(byte[] array, byte element) { + int index = indexOf(array, element); + if (index == INDEX_NOT_FOUND) { + return clone(array); + } + return remove(array, index); + } + + /** + *

Removes the element at the specified position from the specified array. + * All subsequent elements are shifted to the left (subtracts one from + * their indices).

+ * + *

This method returns a new array with the same elements of the input + * array except the element on the specified position. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *

If the input array is {@code null}, an IndexOutOfBoundsException + * will be thrown, because in that case no valid index can be specified.

+ * + *
+     * ArrayUtils.remove(['a'], 0)           = []
+     * ArrayUtils.remove(['a', 'b'], 0)      = ['b']
+     * ArrayUtils.remove(['a', 'b'], 1)      = ['a']
+     * ArrayUtils.remove(['a', 'b', 'c'], 1) = ['a', 'c']
+     * 
+ * + * @param array the array to remove the element from, may not be {@code null} + * @param index the position of the element to be removed + * @return A new array containing the existing elements except the element + * at the specified position. + * @throws IndexOutOfBoundsException if the index is out of range + * (index < 0 || index >= array.length), or if the array is {@code null}. + * @since 2.1 + */ + public static char[] remove(char[] array, int index) { + return (char[]) remove((Object) array, index); + } + + /** + *

Removes the first occurrence of the specified element from the + * specified array. All subsequent elements are shifted to the left + * (subtracts one from their indices). If the array doesn't contains + * such an element, no elements are removed from the array.

+ * + *

This method returns a new array with the same elements of the input + * array except the first occurrence of the specified element. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *
+     * ArrayUtils.removeElement(null, 'a')            = null
+     * ArrayUtils.removeElement([], 'a')              = []
+     * ArrayUtils.removeElement(['a'], 'b')           = ['a']
+     * ArrayUtils.removeElement(['a', 'b'], 'a')      = ['b']
+     * ArrayUtils.removeElement(['a', 'b', 'a'], 'a') = ['b', 'a']
+     * 
+ * + * @param array the array to remove the element from, may be {@code null} + * @param element the element to be removed + * @return A new array containing the existing elements except the first + * occurrence of the specified element. + * @since 2.1 + */ + public static char[] removeElement(char[] array, char element) { + int index = indexOf(array, element); + if (index == INDEX_NOT_FOUND) { + return clone(array); + } + return remove(array, index); + } + + /** + *

Removes the element at the specified position from the specified array. + * All subsequent elements are shifted to the left (subtracts one from + * their indices).

+ * + *

This method returns a new array with the same elements of the input + * array except the element on the specified position. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *

If the input array is {@code null}, an IndexOutOfBoundsException + * will be thrown, because in that case no valid index can be specified.

+ * + *
+     * ArrayUtils.remove([1.1], 0)           = []
+     * ArrayUtils.remove([2.5, 6.0], 0)      = [6.0]
+     * ArrayUtils.remove([2.5, 6.0], 1)      = [2.5]
+     * ArrayUtils.remove([2.5, 6.0, 3.8], 1) = [2.5, 3.8]
+     * 
+ * + * @param array the array to remove the element from, may not be {@code null} + * @param index the position of the element to be removed + * @return A new array containing the existing elements except the element + * at the specified position. + * @throws IndexOutOfBoundsException if the index is out of range + * (index < 0 || index >= array.length), or if the array is {@code null}. + * @since 2.1 + */ + public static double[] remove(double[] array, int index) { + return (double[]) remove((Object) array, index); + } + + /** + *

Removes the first occurrence of the specified element from the + * specified array. All subsequent elements are shifted to the left + * (subtracts one from their indices). If the array doesn't contains + * such an element, no elements are removed from the array.

+ * + *

This method returns a new array with the same elements of the input + * array except the first occurrence of the specified element. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *
+     * ArrayUtils.removeElement(null, 1.1)            = null
+     * ArrayUtils.removeElement([], 1.1)              = []
+     * ArrayUtils.removeElement([1.1], 1.2)           = [1.1]
+     * ArrayUtils.removeElement([1.1, 2.3], 1.1)      = [2.3]
+     * ArrayUtils.removeElement([1.1, 2.3, 1.1], 1.1) = [2.3, 1.1]
+     * 
+ * + * @param array the array to remove the element from, may be {@code null} + * @param element the element to be removed + * @return A new array containing the existing elements except the first + * occurrence of the specified element. + * @since 2.1 + */ + public static double[] removeElement(double[] array, double element) { + int index = indexOf(array, element); + if (index == INDEX_NOT_FOUND) { + return clone(array); + } + return remove(array, index); + } + + /** + *

Removes the element at the specified position from the specified array. + * All subsequent elements are shifted to the left (subtracts one from + * their indices).

+ * + *

This method returns a new array with the same elements of the input + * array except the element on the specified position. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *

If the input array is {@code null}, an IndexOutOfBoundsException + * will be thrown, because in that case no valid index can be specified.

+ * + *
+     * ArrayUtils.remove([1.1], 0)           = []
+     * ArrayUtils.remove([2.5, 6.0], 0)      = [6.0]
+     * ArrayUtils.remove([2.5, 6.0], 1)      = [2.5]
+     * ArrayUtils.remove([2.5, 6.0, 3.8], 1) = [2.5, 3.8]
+     * 
+ * + * @param array the array to remove the element from, may not be {@code null} + * @param index the position of the element to be removed + * @return A new array containing the existing elements except the element + * at the specified position. + * @throws IndexOutOfBoundsException if the index is out of range + * (index < 0 || index >= array.length), or if the array is {@code null}. + * @since 2.1 + */ + public static float[] remove(float[] array, int index) { + return (float[]) remove((Object) array, index); + } + + /** + *

Removes the first occurrence of the specified element from the + * specified array. All subsequent elements are shifted to the left + * (subtracts one from their indices). If the array doesn't contains + * such an element, no elements are removed from the array.

+ * + *

This method returns a new array with the same elements of the input + * array except the first occurrence of the specified element. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *
+     * ArrayUtils.removeElement(null, 1.1)            = null
+     * ArrayUtils.removeElement([], 1.1)              = []
+     * ArrayUtils.removeElement([1.1], 1.2)           = [1.1]
+     * ArrayUtils.removeElement([1.1, 2.3], 1.1)      = [2.3]
+     * ArrayUtils.removeElement([1.1, 2.3, 1.1], 1.1) = [2.3, 1.1]
+     * 
+ * + * @param array the array to remove the element from, may be {@code null} + * @param element the element to be removed + * @return A new array containing the existing elements except the first + * occurrence of the specified element. + * @since 2.1 + */ + public static float[] removeElement(float[] array, float element) { + int index = indexOf(array, element); + if (index == INDEX_NOT_FOUND) { + return clone(array); + } + return remove(array, index); + } + + /** + *

Removes the element at the specified position from the specified array. + * All subsequent elements are shifted to the left (subtracts one from + * their indices).

+ * + *

This method returns a new array with the same elements of the input + * array except the element on the specified position. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *

If the input array is {@code null}, an IndexOutOfBoundsException + * will be thrown, because in that case no valid index can be specified.

+ * + *
+     * ArrayUtils.remove([1], 0)         = []
+     * ArrayUtils.remove([2, 6], 0)      = [6]
+     * ArrayUtils.remove([2, 6], 1)      = [2]
+     * ArrayUtils.remove([2, 6, 3], 1)   = [2, 3]
+     * 
+ * + * @param array the array to remove the element from, may not be {@code null} + * @param index the position of the element to be removed + * @return A new array containing the existing elements except the element + * at the specified position. + * @throws IndexOutOfBoundsException if the index is out of range + * (index < 0 || index >= array.length), or if the array is {@code null}. + * @since 2.1 + */ + public static int[] remove(int[] array, int index) { + return (int[]) remove((Object) array, index); + } + + /** + *

Removes the first occurrence of the specified element from the + * specified array. All subsequent elements are shifted to the left + * (subtracts one from their indices). If the array doesn't contains + * such an element, no elements are removed from the array.

+ * + *

This method returns a new array with the same elements of the input + * array except the first occurrence of the specified element. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *
+     * ArrayUtils.removeElement(null, 1)      = null
+     * ArrayUtils.removeElement([], 1)        = []
+     * ArrayUtils.removeElement([1], 2)       = [1]
+     * ArrayUtils.removeElement([1, 3], 1)    = [3]
+     * ArrayUtils.removeElement([1, 3, 1], 1) = [3, 1]
+     * 
+ * + * @param array the array to remove the element from, may be {@code null} + * @param element the element to be removed + * @return A new array containing the existing elements except the first + * occurrence of the specified element. + * @since 2.1 + */ + public static int[] removeElement(int[] array, int element) { + int index = indexOf(array, element); + if (index == INDEX_NOT_FOUND) { + return clone(array); + } + return remove(array, index); + } + + /** + *

Removes the element at the specified position from the specified array. + * All subsequent elements are shifted to the left (subtracts one from + * their indices).

+ * + *

This method returns a new array with the same elements of the input + * array except the element on the specified position. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *

If the input array is {@code null}, an IndexOutOfBoundsException + * will be thrown, because in that case no valid index can be specified.

+ * + *
+     * ArrayUtils.remove([1], 0)         = []
+     * ArrayUtils.remove([2, 6], 0)      = [6]
+     * ArrayUtils.remove([2, 6], 1)      = [2]
+     * ArrayUtils.remove([2, 6, 3], 1)   = [2, 3]
+     * 
+ * + * @param array the array to remove the element from, may not be {@code null} + * @param index the position of the element to be removed + * @return A new array containing the existing elements except the element + * at the specified position. + * @throws IndexOutOfBoundsException if the index is out of range + * (index < 0 || index >= array.length), or if the array is {@code null}. + * @since 2.1 + */ + public static long[] remove(long[] array, int index) { + return (long[]) remove((Object) array, index); + } + + /** + *

Removes the first occurrence of the specified element from the + * specified array. All subsequent elements are shifted to the left + * (subtracts one from their indices). If the array doesn't contains + * such an element, no elements are removed from the array.

+ * + *

This method returns a new array with the same elements of the input + * array except the first occurrence of the specified element. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *
+     * ArrayUtils.removeElement(null, 1)      = null
+     * ArrayUtils.removeElement([], 1)        = []
+     * ArrayUtils.removeElement([1], 2)       = [1]
+     * ArrayUtils.removeElement([1, 3], 1)    = [3]
+     * ArrayUtils.removeElement([1, 3, 1], 1) = [3, 1]
+     * 
+ * + * @param array the array to remove the element from, may be {@code null} + * @param element the element to be removed + * @return A new array containing the existing elements except the first + * occurrence of the specified element. + * @since 2.1 + */ + public static long[] removeElement(long[] array, long element) { + int index = indexOf(array, element); + if (index == INDEX_NOT_FOUND) { + return clone(array); + } + return remove(array, index); + } + + /** + *

Removes the element at the specified position from the specified array. + * All subsequent elements are shifted to the left (subtracts one from + * their indices).

+ * + *

This method returns a new array with the same elements of the input + * array except the element on the specified position. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *

If the input array is {@code null}, an IndexOutOfBoundsException + * will be thrown, because in that case no valid index can be specified.

+ * + *
+     * ArrayUtils.remove([1], 0)         = []
+     * ArrayUtils.remove([2, 6], 0)      = [6]
+     * ArrayUtils.remove([2, 6], 1)      = [2]
+     * ArrayUtils.remove([2, 6, 3], 1)   = [2, 3]
+     * 
+ * + * @param array the array to remove the element from, may not be {@code null} + * @param index the position of the element to be removed + * @return A new array containing the existing elements except the element + * at the specified position. + * @throws IndexOutOfBoundsException if the index is out of range + * (index < 0 || index >= array.length), or if the array is {@code null}. + * @since 2.1 + */ + public static short[] remove(short[] array, int index) { + return (short[]) remove((Object) array, index); + } + + /** + *

Removes the first occurrence of the specified element from the + * specified array. All subsequent elements are shifted to the left + * (subtracts one from their indices). If the array doesn't contains + * such an element, no elements are removed from the array.

+ * + *

This method returns a new array with the same elements of the input + * array except the first occurrence of the specified element. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *
+     * ArrayUtils.removeElement(null, 1)      = null
+     * ArrayUtils.removeElement([], 1)        = []
+     * ArrayUtils.removeElement([1], 2)       = [1]
+     * ArrayUtils.removeElement([1, 3], 1)    = [3]
+     * ArrayUtils.removeElement([1, 3, 1], 1) = [3, 1]
+     * 
+ * + * @param array the array to remove the element from, may be {@code null} + * @param element the element to be removed + * @return A new array containing the existing elements except the first + * occurrence of the specified element. + * @since 2.1 + */ + public static short[] removeElement(short[] array, short element) { + int index = indexOf(array, element); + if (index == INDEX_NOT_FOUND) { + return clone(array); + } + return remove(array, index); + } + + /** + *

Removes the element at the specified position from the specified array. + * All subsequent elements are shifted to the left (subtracts one from + * their indices).

+ * + *

This method returns a new array with the same elements of the input + * array except the element on the specified position. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *

If the input array is {@code null}, an IndexOutOfBoundsException + * will be thrown, because in that case no valid index can be specified.

+ * + * @param array the array to remove the element from, may not be {@code null} + * @param index the position of the element to be removed + * @return A new array containing the existing elements except the element + * at the specified position. + * @throws IndexOutOfBoundsException if the index is out of range + * (index < 0 || index >= array.length), or if the array is {@code null}. + * @since 2.1 + */ + private static Object remove(Object array, int index) { + int length = getLength(array); + if (index < 0 || index >= length) { + throw new IndexOutOfBoundsException("Index: " + index + ", Length: " + length); + } + + Object result = Array.newInstance(array.getClass().getComponentType(), length - 1); + System.arraycopy(array, 0, result, 0, index); + if (index < length - 1) { + System.arraycopy(array, index + 1, result, index, length - index - 1); + } + + return result; + } + + /** + *

Removes the elements at the specified positions from the specified array. + * All remaining elements are shifted to the left.

+ * + *

This method returns a new array with the same elements of the input + * array except those at the specified positions. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *

If the input array is {@code null}, an IndexOutOfBoundsException + * will be thrown, because in that case no valid index can be specified.

+ * + *
+     * ArrayUtils.removeAll(["a", "b", "c"], 0, 2) = ["b"]
+     * ArrayUtils.removeAll(["a", "b", "c"], 1, 2) = ["a"]
+     * 
+ * + * @param the component type of the array + * @param array the array to remove the element from, may not be {@code null} + * @param indices the positions of the elements to be removed + * @return A new array containing the existing elements except those + * at the specified positions. + * @throws IndexOutOfBoundsException if any index is out of range + * (index < 0 || index >= array.length), or if the array is {@code null}. + * @since 3.0.1 + */ + @SuppressWarnings("unchecked") + // removeAll() always creates an array of the same type as its input + public static T[] removeAll(T[] array, int... indices) { + return (T[]) removeAll((Object) array, clone(indices)); + } + + /** + *

Removes occurrences of specified elements, in specified quantities, + * from the specified array. All subsequent elements are shifted left. + * For any element-to-be-removed specified in greater quantities than + * contained in the original array, no change occurs beyond the + * removal of the existing matching items.

+ * + *

This method returns a new array with the same elements of the input + * array except for the earliest-encountered occurrences of the specified + * elements. The component type of the returned array is always the same + * as that of the input array.

+ * + *
+     * ArrayUtils.removeElements(null, "a", "b")            = null
+     * ArrayUtils.removeElements([], "a", "b")              = []
+     * ArrayUtils.removeElements(["a"], "b", "c")           = ["a"]
+     * ArrayUtils.removeElements(["a", "b"], "a", "c")      = ["b"]
+     * ArrayUtils.removeElements(["a", "b", "a"], "a")      = ["b", "a"]
+     * ArrayUtils.removeElements(["a", "b", "a"], "a", "a") = ["b"]
+     * 
+ * + * @param the component type of the array + * @param array the array to remove the element from, may be {@code null} + * @param values the elements to be removed + * @return A new array containing the existing elements except the + * earliest-encountered occurrences of the specified elements. + * @since 3.0.1 + */ + public static T[] removeElements(T[] array, T... values) { + if (isEmpty(array) || isEmpty(values)) { + return clone(array); + } + HashMap occurrences = new HashMap(values.length); + for (T v : values) { + MutableInt count = occurrences.get(v); + if (count == null) { + occurrences.put(v, new MutableInt(1)); + } else { + count.increment(); + } + } + HashSet toRemove = new HashSet(); + for (Map.Entry e : occurrences.entrySet()) { + T v = e.getKey(); + int found = 0; + for (int i = 0, ct = e.getValue().intValue(); i < ct; i++) { + found = indexOf(array, v, found); + if (found < 0) { + break; + } + toRemove.add(found++); + } + } + return removeAll(array, extractIndices(toRemove)); + } + + /** + *

Removes the elements at the specified positions from the specified array. + * All remaining elements are shifted to the left.

+ * + *

This method returns a new array with the same elements of the input + * array except those at the specified positions. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *

If the input array is {@code null}, an IndexOutOfBoundsException + * will be thrown, because in that case no valid index can be specified.

+ * + *
+     * ArrayUtils.removeAll([1], 0)             = []
+     * ArrayUtils.removeAll([2, 6], 0)          = [6]
+     * ArrayUtils.removeAll([2, 6], 0, 1)       = []
+     * ArrayUtils.removeAll([2, 6, 3], 1, 2)    = [2]
+     * ArrayUtils.removeAll([2, 6, 3], 0, 2)    = [6]
+     * ArrayUtils.removeAll([2, 6, 3], 0, 1, 2) = []
+     * 
+ * + * @param array the array to remove the element from, may not be {@code null} + * @param indices the positions of the elements to be removed + * @return A new array containing the existing elements except those + * at the specified positions. + * @throws IndexOutOfBoundsException if any index is out of range + * (index < 0 || index >= array.length), or if the array is {@code null}. + * @since 3.0.1 + */ + public static byte[] removeAll(byte[] array, int... indices) { + return (byte[]) removeAll((Object) array, clone(indices)); + } + + /** + *

Removes occurrences of specified elements, in specified quantities, + * from the specified array. All subsequent elements are shifted left. + * For any element-to-be-removed specified in greater quantities than + * contained in the original array, no change occurs beyond the + * removal of the existing matching items.

+ * + *

This method returns a new array with the same elements of the input + * array except for the earliest-encountered occurrences of the specified + * elements. The component type of the returned array is always the same + * as that of the input array.

+ * + *
+     * ArrayUtils.removeElements(null, 1, 2)      = null
+     * ArrayUtils.removeElements([], 1, 2)        = []
+     * ArrayUtils.removeElements([1], 2, 3)       = [1]
+     * ArrayUtils.removeElements([1, 3], 1, 2)    = [3]
+     * ArrayUtils.removeElements([1, 3, 1], 1)    = [3, 1]
+     * ArrayUtils.removeElements([1, 3, 1], 1, 1) = [3]
+     * 
+ * + * @param array the array to remove the element from, may be {@code null} + * @param values the elements to be removed + * @return A new array containing the existing elements except the + * earliest-encountered occurrences of the specified elements. + * @since 3.0.1 + */ + public static byte[] removeElements(byte[] array, byte... values) { + if (isEmpty(array) || isEmpty(values)) { + return clone(array); + } + HashMap occurrences = new HashMap(values.length); + for (byte v : values) { + Byte boxed = Byte.valueOf(v); + MutableInt count = occurrences.get(boxed); + if (count == null) { + occurrences.put(boxed, new MutableInt(1)); + } else { + count.increment(); + } + } + HashSet toRemove = new HashSet(); + for (Map.Entry e : occurrences.entrySet()) { + Byte v = e.getKey(); + int found = 0; + for (int i = 0, ct = e.getValue().intValue(); i < ct; i++) { + found = indexOf(array, v.byteValue(), found); + if (found < 0) { + break; + } + toRemove.add(found++); + } + } + return removeAll(array, extractIndices(toRemove)); + } + + /** + *

Removes the elements at the specified positions from the specified array. + * All remaining elements are shifted to the left.

+ * + *

This method returns a new array with the same elements of the input + * array except those at the specified positions. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *

If the input array is {@code null}, an IndexOutOfBoundsException + * will be thrown, because in that case no valid index can be specified.

+ * + *
+     * ArrayUtils.removeAll([1], 0)             = []
+     * ArrayUtils.removeAll([2, 6], 0)          = [6]
+     * ArrayUtils.removeAll([2, 6], 0, 1)       = []
+     * ArrayUtils.removeAll([2, 6, 3], 1, 2)    = [2]
+     * ArrayUtils.removeAll([2, 6, 3], 0, 2)    = [6]
+     * ArrayUtils.removeAll([2, 6, 3], 0, 1, 2) = []
+     * 
+ * + * @param array the array to remove the element from, may not be {@code null} + * @param indices the positions of the elements to be removed + * @return A new array containing the existing elements except those + * at the specified positions. + * @throws IndexOutOfBoundsException if any index is out of range + * (index < 0 || index >= array.length), or if the array is {@code null}. + * @since 3.0.1 + */ + public static short[] removeAll(short[] array, int... indices) { + return (short[]) removeAll((Object) array, clone(indices)); + } + + /** + *

Removes occurrences of specified elements, in specified quantities, + * from the specified array. All subsequent elements are shifted left. + * For any element-to-be-removed specified in greater quantities than + * contained in the original array, no change occurs beyond the + * removal of the existing matching items.

+ * + *

This method returns a new array with the same elements of the input + * array except for the earliest-encountered occurrences of the specified + * elements. The component type of the returned array is always the same + * as that of the input array.

+ * + *
+     * ArrayUtils.removeElements(null, 1, 2)      = null
+     * ArrayUtils.removeElements([], 1, 2)        = []
+     * ArrayUtils.removeElements([1], 2, 3)       = [1]
+     * ArrayUtils.removeElements([1, 3], 1, 2)    = [3]
+     * ArrayUtils.removeElements([1, 3, 1], 1)    = [3, 1]
+     * ArrayUtils.removeElements([1, 3, 1], 1, 1) = [3]
+     * 
+ * + * @param array the array to remove the element from, may be {@code null} + * @param values the elements to be removed + * @return A new array containing the existing elements except the + * earliest-encountered occurrences of the specified elements. + * @since 3.0.1 + */ + public static short[] removeElements(short[] array, short... values) { + if (isEmpty(array) || isEmpty(values)) { + return clone(array); + } + HashMap occurrences = new HashMap(values.length); + for (short v : values) { + Short boxed = Short.valueOf(v); + MutableInt count = occurrences.get(boxed); + if (count == null) { + occurrences.put(boxed, new MutableInt(1)); + } else { + count.increment(); + } + } + HashSet toRemove = new HashSet(); + for (Map.Entry e : occurrences.entrySet()) { + Short v = e.getKey(); + int found = 0; + for (int i = 0, ct = e.getValue().intValue(); i < ct; i++) { + found = indexOf(array, v.shortValue(), found); + if (found < 0) { + break; + } + toRemove.add(found++); + } + } + return removeAll(array, extractIndices(toRemove)); + } + + /** + *

Removes the elements at the specified positions from the specified array. + * All remaining elements are shifted to the left.

+ * + *

This method returns a new array with the same elements of the input + * array except those at the specified positions. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *

If the input array is {@code null}, an IndexOutOfBoundsException + * will be thrown, because in that case no valid index can be specified.

+ * + *
+     * ArrayUtils.removeAll([1], 0)             = []
+     * ArrayUtils.removeAll([2, 6], 0)          = [6]
+     * ArrayUtils.removeAll([2, 6], 0, 1)       = []
+     * ArrayUtils.removeAll([2, 6, 3], 1, 2)    = [2]
+     * ArrayUtils.removeAll([2, 6, 3], 0, 2)    = [6]
+     * ArrayUtils.removeAll([2, 6, 3], 0, 1, 2) = []
+     * 
+ * + * @param array the array to remove the element from, may not be {@code null} + * @param indices the positions of the elements to be removed + * @return A new array containing the existing elements except those + * at the specified positions. + * @throws IndexOutOfBoundsException if any index is out of range + * (index < 0 || index >= array.length), or if the array is {@code null}. + * @since 3.0.1 + */ + public static int[] removeAll(int[] array, int... indices) { + return (int[]) removeAll((Object) array, clone(indices)); + } + + /** + *

Removes occurrences of specified elements, in specified quantities, + * from the specified array. All subsequent elements are shifted left. + * For any element-to-be-removed specified in greater quantities than + * contained in the original array, no change occurs beyond the + * removal of the existing matching items.

+ * + *

This method returns a new array with the same elements of the input + * array except for the earliest-encountered occurrences of the specified + * elements. The component type of the returned array is always the same + * as that of the input array.

+ * + *
+     * ArrayUtils.removeElements(null, 1, 2)      = null
+     * ArrayUtils.removeElements([], 1, 2)        = []
+     * ArrayUtils.removeElements([1], 2, 3)       = [1]
+     * ArrayUtils.removeElements([1, 3], 1, 2)    = [3]
+     * ArrayUtils.removeElements([1, 3, 1], 1)    = [3, 1]
+     * ArrayUtils.removeElements([1, 3, 1], 1, 1) = [3]
+     * 
+ * + * @param array the array to remove the element from, may be {@code null} + * @param values the elements to be removed + * @return A new array containing the existing elements except the + * earliest-encountered occurrences of the specified elements. + * @since 3.0.1 + */ + public static int[] removeElements(int[] array, int... values) { + if (isEmpty(array) || isEmpty(values)) { + return clone(array); + } + HashMap occurrences = new HashMap(values.length); + for (int v : values) { + Integer boxed = Integer.valueOf(v); + MutableInt count = occurrences.get(boxed); + if (count == null) { + occurrences.put(boxed, new MutableInt(1)); + } else { + count.increment(); + } + } + HashSet toRemove = new HashSet(); + for (Map.Entry e : occurrences.entrySet()) { + Integer v = e.getKey(); + int found = 0; + for (int i = 0, ct = e.getValue().intValue(); i < ct; i++) { + found = indexOf(array, v.intValue(), found); + if (found < 0) { + break; + } + toRemove.add(found++); + } + } + return removeAll(array, extractIndices(toRemove)); + } + + /** + *

Removes the elements at the specified positions from the specified array. + * All remaining elements are shifted to the left.

+ * + *

This method returns a new array with the same elements of the input + * array except those at the specified positions. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *

If the input array is {@code null}, an IndexOutOfBoundsException + * will be thrown, because in that case no valid index can be specified.

+ * + *
+     * ArrayUtils.removeAll([1], 0)             = []
+     * ArrayUtils.removeAll([2, 6], 0)          = [6]
+     * ArrayUtils.removeAll([2, 6], 0, 1)       = []
+     * ArrayUtils.removeAll([2, 6, 3], 1, 2)    = [2]
+     * ArrayUtils.removeAll([2, 6, 3], 0, 2)    = [6]
+     * ArrayUtils.removeAll([2, 6, 3], 0, 1, 2) = []
+     * 
+ * + * @param array the array to remove the element from, may not be {@code null} + * @param indices the positions of the elements to be removed + * @return A new array containing the existing elements except those + * at the specified positions. + * @throws IndexOutOfBoundsException if any index is out of range + * (index < 0 || index >= array.length), or if the array is {@code null}. + * @since 3.0.1 + */ + public static char[] removeAll(char[] array, int... indices) { + return (char[]) removeAll((Object) array, clone(indices)); + } + + /** + *

Removes occurrences of specified elements, in specified quantities, + * from the specified array. All subsequent elements are shifted left. + * For any element-to-be-removed specified in greater quantities than + * contained in the original array, no change occurs beyond the + * removal of the existing matching items.

+ * + *

This method returns a new array with the same elements of the input + * array except for the earliest-encountered occurrences of the specified + * elements. The component type of the returned array is always the same + * as that of the input array.

+ * + *
+     * ArrayUtils.removeElements(null, 1, 2)      = null
+     * ArrayUtils.removeElements([], 1, 2)        = []
+     * ArrayUtils.removeElements([1], 2, 3)       = [1]
+     * ArrayUtils.removeElements([1, 3], 1, 2)    = [3]
+     * ArrayUtils.removeElements([1, 3, 1], 1)    = [3, 1]
+     * ArrayUtils.removeElements([1, 3, 1], 1, 1) = [3]
+     * 
+ * + * @param array the array to remove the element from, may be {@code null} + * @param values the elements to be removed + * @return A new array containing the existing elements except the + * earliest-encountered occurrences of the specified elements. + * @since 3.0.1 + */ + public static char[] removeElements(char[] array, char... values) { + if (isEmpty(array) || isEmpty(values)) { + return clone(array); + } + HashMap occurrences = new HashMap(values.length); + for (char v : values) { + Character boxed = Character.valueOf(v); + MutableInt count = occurrences.get(boxed); + if (count == null) { + occurrences.put(boxed, new MutableInt(1)); + } else { + count.increment(); + } + } + HashSet toRemove = new HashSet(); + for (Map.Entry e : occurrences.entrySet()) { + Character v = e.getKey(); + int found = 0; + for (int i = 0, ct = e.getValue().intValue(); i < ct; i++) { + found = indexOf(array, v.charValue(), found); + if (found < 0) { + break; + } + toRemove.add(found++); + } + } + return removeAll(array, extractIndices(toRemove)); + } + + /** + *

Removes the elements at the specified positions from the specified array. + * All remaining elements are shifted to the left.

+ * + *

This method returns a new array with the same elements of the input + * array except those at the specified positions. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *

If the input array is {@code null}, an IndexOutOfBoundsException + * will be thrown, because in that case no valid index can be specified.

+ * + *
+     * ArrayUtils.removeAll([1], 0)             = []
+     * ArrayUtils.removeAll([2, 6], 0)          = [6]
+     * ArrayUtils.removeAll([2, 6], 0, 1)       = []
+     * ArrayUtils.removeAll([2, 6, 3], 1, 2)    = [2]
+     * ArrayUtils.removeAll([2, 6, 3], 0, 2)    = [6]
+     * ArrayUtils.removeAll([2, 6, 3], 0, 1, 2) = []
+     * 
+ * + * @param array the array to remove the element from, may not be {@code null} + * @param indices the positions of the elements to be removed + * @return A new array containing the existing elements except those + * at the specified positions. + * @throws IndexOutOfBoundsException if any index is out of range + * (index < 0 || index >= array.length), or if the array is {@code null}. + * @since 3.0.1 + */ + public static long[] removeAll(long[] array, int... indices) { + return (long[]) removeAll((Object) array, clone(indices)); + } + + /** + *

Removes occurrences of specified elements, in specified quantities, + * from the specified array. All subsequent elements are shifted left. + * For any element-to-be-removed specified in greater quantities than + * contained in the original array, no change occurs beyond the + * removal of the existing matching items.

+ * + *

This method returns a new array with the same elements of the input + * array except for the earliest-encountered occurrences of the specified + * elements. The component type of the returned array is always the same + * as that of the input array.

+ * + *
+     * ArrayUtils.removeElements(null, 1, 2)      = null
+     * ArrayUtils.removeElements([], 1, 2)        = []
+     * ArrayUtils.removeElements([1], 2, 3)       = [1]
+     * ArrayUtils.removeElements([1, 3], 1, 2)    = [3]
+     * ArrayUtils.removeElements([1, 3, 1], 1)    = [3, 1]
+     * ArrayUtils.removeElements([1, 3, 1], 1, 1) = [3]
+     * 
+ * + * @param array the array to remove the element from, may be {@code null} + * @param values the elements to be removed + * @return A new array containing the existing elements except the + * earliest-encountered occurrences of the specified elements. + * @since 3.0.1 + */ + public static long[] removeElements(long[] array, long... values) { + if (isEmpty(array) || isEmpty(values)) { + return clone(array); + } + HashMap occurrences = new HashMap(values.length); + for (long v : values) { + Long boxed = Long.valueOf(v); + MutableInt count = occurrences.get(boxed); + if (count == null) { + occurrences.put(boxed, new MutableInt(1)); + } else { + count.increment(); + } + } + HashSet toRemove = new HashSet(); + for (Map.Entry e : occurrences.entrySet()) { + Long v = e.getKey(); + int found = 0; + for (int i = 0, ct = e.getValue().intValue(); i < ct; i++) { + found = indexOf(array, v.longValue(), found); + if (found < 0) { + break; + } + toRemove.add(found++); + } + } + return removeAll(array, extractIndices(toRemove)); + } + + /** + *

Removes the elements at the specified positions from the specified array. + * All remaining elements are shifted to the left.

+ * + *

This method returns a new array with the same elements of the input + * array except those at the specified positions. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *

If the input array is {@code null}, an IndexOutOfBoundsException + * will be thrown, because in that case no valid index can be specified.

+ * + *
+     * ArrayUtils.removeAll([1], 0)             = []
+     * ArrayUtils.removeAll([2, 6], 0)          = [6]
+     * ArrayUtils.removeAll([2, 6], 0, 1)       = []
+     * ArrayUtils.removeAll([2, 6, 3], 1, 2)    = [2]
+     * ArrayUtils.removeAll([2, 6, 3], 0, 2)    = [6]
+     * ArrayUtils.removeAll([2, 6, 3], 0, 1, 2) = []
+     * 
+ * + * @param array the array to remove the element from, may not be {@code null} + * @param indices the positions of the elements to be removed + * @return A new array containing the existing elements except those + * at the specified positions. + * @throws IndexOutOfBoundsException if any index is out of range + * (index < 0 || index >= array.length), or if the array is {@code null}. + * @since 3.0.1 + */ + public static float[] removeAll(float[] array, int... indices) { + return (float[]) removeAll((Object) array, clone(indices)); + } + + /** + *

Removes occurrences of specified elements, in specified quantities, + * from the specified array. All subsequent elements are shifted left. + * For any element-to-be-removed specified in greater quantities than + * contained in the original array, no change occurs beyond the + * removal of the existing matching items.

+ * + *

This method returns a new array with the same elements of the input + * array except for the earliest-encountered occurrences of the specified + * elements. The component type of the returned array is always the same + * as that of the input array.

+ * + *
+     * ArrayUtils.removeElements(null, 1, 2)      = null
+     * ArrayUtils.removeElements([], 1, 2)        = []
+     * ArrayUtils.removeElements([1], 2, 3)       = [1]
+     * ArrayUtils.removeElements([1, 3], 1, 2)    = [3]
+     * ArrayUtils.removeElements([1, 3, 1], 1)    = [3, 1]
+     * ArrayUtils.removeElements([1, 3, 1], 1, 1) = [3]
+     * 
+ * + * @param array the array to remove the element from, may be {@code null} + * @param values the elements to be removed + * @return A new array containing the existing elements except the + * earliest-encountered occurrences of the specified elements. + * @since 3.0.1 + */ + public static float[] removeElements(float[] array, float... values) { + if (isEmpty(array) || isEmpty(values)) { + return clone(array); + } + HashMap occurrences = new HashMap(values.length); + for (float v : values) { + Float boxed = Float.valueOf(v); + MutableInt count = occurrences.get(boxed); + if (count == null) { + occurrences.put(boxed, new MutableInt(1)); + } else { + count.increment(); + } + } + HashSet toRemove = new HashSet(); + for (Map.Entry e : occurrences.entrySet()) { + Float v = e.getKey(); + int found = 0; + for (int i = 0, ct = e.getValue().intValue(); i < ct; i++) { + found = indexOf(array, v.floatValue(), found); + if (found < 0) { + break; + } + toRemove.add(found++); + } + } + return removeAll(array, extractIndices(toRemove)); + } + + /** + *

Removes the elements at the specified positions from the specified array. + * All remaining elements are shifted to the left.

+ * + *

This method returns a new array with the same elements of the input + * array except those at the specified positions. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *

If the input array is {@code null}, an IndexOutOfBoundsException + * will be thrown, because in that case no valid index can be specified.

+ * + *
+     * ArrayUtils.removeAll([1], 0)             = []
+     * ArrayUtils.removeAll([2, 6], 0)          = [6]
+     * ArrayUtils.removeAll([2, 6], 0, 1)       = []
+     * ArrayUtils.removeAll([2, 6, 3], 1, 2)    = [2]
+     * ArrayUtils.removeAll([2, 6, 3], 0, 2)    = [6]
+     * ArrayUtils.removeAll([2, 6, 3], 0, 1, 2) = []
+     * 
+ * + * @param array the array to remove the element from, may not be {@code null} + * @param indices the positions of the elements to be removed + * @return A new array containing the existing elements except those + * at the specified positions. + * @throws IndexOutOfBoundsException if any index is out of range + * (index < 0 || index >= array.length), or if the array is {@code null}. + * @since 3.0.1 + */ + public static double[] removeAll(double[] array, int... indices) { + return (double[]) removeAll((Object) array, clone(indices)); + } + + /** + *

Removes occurrences of specified elements, in specified quantities, + * from the specified array. All subsequent elements are shifted left. + * For any element-to-be-removed specified in greater quantities than + * contained in the original array, no change occurs beyond the + * removal of the existing matching items.

+ * + *

This method returns a new array with the same elements of the input + * array except for the earliest-encountered occurrences of the specified + * elements. The component type of the returned array is always the same + * as that of the input array.

+ * + *
+     * ArrayUtils.removeElements(null, 1, 2)      = null
+     * ArrayUtils.removeElements([], 1, 2)        = []
+     * ArrayUtils.removeElements([1], 2, 3)       = [1]
+     * ArrayUtils.removeElements([1, 3], 1, 2)    = [3]
+     * ArrayUtils.removeElements([1, 3, 1], 1)    = [3, 1]
+     * ArrayUtils.removeElements([1, 3, 1], 1, 1) = [3]
+     * 
+ * + * @param array the array to remove the element from, may be {@code null} + * @param values the elements to be removed + * @return A new array containing the existing elements except the + * earliest-encountered occurrences of the specified elements. + * @since 3.0.1 + */ + public static double[] removeElements(double[] array, double... values) { + if (isEmpty(array) || isEmpty(values)) { + return clone(array); + } + HashMap occurrences = new HashMap(values.length); + for (double v : values) { + Double boxed = Double.valueOf(v); + MutableInt count = occurrences.get(boxed); + if (count == null) { + occurrences.put(boxed, new MutableInt(1)); + } else { + count.increment(); + } + } + HashSet toRemove = new HashSet(); + for (Map.Entry e : occurrences.entrySet()) { + Double v = e.getKey(); + int found = 0; + for (int i = 0, ct = e.getValue().intValue(); i < ct; i++) { + found = indexOf(array, v.doubleValue(), found); + if (found < 0) { + break; + } + toRemove.add(found++); + } + } + return removeAll(array, extractIndices(toRemove)); + } + + /** + *

Removes the elements at the specified positions from the specified array. + * All remaining elements are shifted to the left.

+ * + *

This method returns a new array with the same elements of the input + * array except those at the specified positions. The component + * type of the returned array is always the same as that of the input + * array.

+ * + *

If the input array is {@code null}, an IndexOutOfBoundsException + * will be thrown, because in that case no valid index can be specified.

+ * + *
+     * ArrayUtils.removeAll([true, false, true], 0, 2) = [false]
+     * ArrayUtils.removeAll([true, false, true], 1, 2) = [true]
+     * 
+ * + * @param array the array to remove the element from, may not be {@code null} + * @param indices the positions of the elements to be removed + * @return A new array containing the existing elements except those + * at the specified positions. + * @throws IndexOutOfBoundsException if any index is out of range + * (index < 0 || index >= array.length), or if the array is {@code null}. + * @since 3.0.1 + */ + public static boolean[] removeAll(boolean[] array, int... indices) { + return (boolean[]) removeAll((Object) array, clone(indices)); + } + + /** + *

Removes occurrences of specified elements, in specified quantities, + * from the specified array. All subsequent elements are shifted left. + * For any element-to-be-removed specified in greater quantities than + * contained in the original array, no change occurs beyond the + * removal of the existing matching items.

+ * + *

This method returns a new array with the same elements of the input + * array except for the earliest-encountered occurrences of the specified + * elements. The component type of the returned array is always the same + * as that of the input array.

+ * + *
+     * ArrayUtils.removeElements(null, true, false)               = null
+     * ArrayUtils.removeElements([], true, false)                 = []
+     * ArrayUtils.removeElements([true], false, false)            = [true]
+     * ArrayUtils.removeElements([true, false], true, true)       = [false]
+     * ArrayUtils.removeElements([true, false, true], true)       = [false, true]
+     * ArrayUtils.removeElements([true, false, true], true, true) = [false]
+     * 
+ * + * @param array the array to remove the element from, may be {@code null} + * @param values the elements to be removed + * @return A new array containing the existing elements except the + * earliest-encountered occurrences of the specified elements. + * @since 3.0.1 + */ + public static boolean[] removeElements(boolean[] array, boolean... values) { + if (isEmpty(array) || isEmpty(values)) { + return clone(array); + } + HashMap occurrences = new HashMap(values.length); + for (boolean v : values) { + Boolean boxed = Boolean.valueOf(v); + MutableInt count = occurrences.get(boxed); + if (count == null) { + occurrences.put(boxed, new MutableInt(1)); + } else { + count.increment(); + } + } + HashSet toRemove = new HashSet(); + for (Map.Entry e : occurrences.entrySet()) { + Boolean v = e.getKey(); + int found = 0; + for (int i = 0, ct = e.getValue().intValue(); i < ct; i++) { + found = indexOf(array, v.booleanValue(), found); + if (found < 0) { + break; + } + toRemove.add(found++); + } + } + return removeAll(array, extractIndices(toRemove)); + } + + /** + * Removes multiple array elements specified by index. + * @param array source + * @param indices to remove, WILL BE SORTED--so only clones of user-owned arrays! + * @return new array of same type minus elements specified by unique values of {@code indices} + * @since 3.0.1 + */ + private static Object removeAll(Object array, int... indices) { + int length = getLength(array); + int diff = 0; + + if (isNotEmpty(indices)) { + Arrays.sort(indices); + + int i = indices.length; + int prevIndex = length; + while (--i >= 0) { + int index = indices[i]; + if (index < 0 || index >= length) { + throw new IndexOutOfBoundsException("Index: " + index + ", Length: " + length); + } + if (index >= prevIndex) { + continue; + } + diff++; + prevIndex = index; + } + } + Object result = Array.newInstance(array.getClass().getComponentType(), length - diff); + if (diff < length) { + int end = length; + int dest = length - diff; + for (int i = indices.length - 1; i >= 0; i--) { + int index = indices[i]; + if (end - index > 1) { + int cp = end - index - 1; + dest -= cp; + System.arraycopy(array, index + 1, result, dest, cp); + } + end = index; + } + if (end > 0) { + System.arraycopy(array, 0, result, 0, end); + } + } + return result; + } + + /** + * Extract a set of Integer indices into an int[]. + * @param coll {@code HashSet} of {@code Integer} + * @return int[] + * @since 3.0.1 + */ + private static int[] extractIndices(HashSet coll) { + int[] result = new int[coll.size()]; + int i = 0; + for (Integer index : coll) { + result[i++] = index.intValue(); + } + return result; + } +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/ArrayUtilsAddTest.java b/ApacheCommonsLang/org/apache/commons/lang3/ArrayUtilsAddTest.java new file mode 100644 index 0000000..26bebb8 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/ArrayUtilsAddTest.java @@ -0,0 +1,610 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.lang3; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.util.Arrays; + +import org.junit.Test; + +/** + * Tests ArrayUtils add methods. + * + * @version $Id: ArrayUtilsAddTest.java 1199894 2011-11-09 17:53:59Z ggregory $ + */ +public class ArrayUtilsAddTest { + + @Test + public void testJira567(){ + Number[] n; + // Valid array construction + n = ArrayUtils.addAll(new Number[]{Integer.valueOf(1)}, new Long[]{Long.valueOf(2)}); + assertEquals(2,n.length); + assertEquals(Number.class,n.getClass().getComponentType()); + try { + // Invalid - can't store Long in Integer array + n = ArrayUtils.addAll(new Integer[]{Integer.valueOf(1)}, new Long[]{Long.valueOf(2)}); + fail("Should have generated IllegalArgumentException"); + } catch (IllegalArgumentException expected) { + } + } + + @Test + public void testAddObjectArrayBoolean() { + boolean[] newArray; + newArray = ArrayUtils.add((boolean[])null, false); + assertTrue(Arrays.equals(new boolean[]{false}, newArray)); + assertEquals(Boolean.TYPE, newArray.getClass().getComponentType()); + newArray = ArrayUtils.add((boolean[])null, true); + assertTrue(Arrays.equals(new boolean[]{true}, newArray)); + assertEquals(Boolean.TYPE, newArray.getClass().getComponentType()); + boolean[] array1 = new boolean[]{true, false, true}; + newArray = ArrayUtils.add(array1, false); + assertTrue(Arrays.equals(new boolean[]{true, false, true, false}, newArray)); + assertEquals(Boolean.TYPE, newArray.getClass().getComponentType()); + } + + @Test + public void testAddObjectArrayByte() { + byte[] newArray; + newArray = ArrayUtils.add((byte[])null, (byte)0); + assertTrue(Arrays.equals(new byte[]{0}, newArray)); + assertEquals(Byte.TYPE, newArray.getClass().getComponentType()); + newArray = ArrayUtils.add((byte[])null, (byte)1); + assertTrue(Arrays.equals(new byte[]{1}, newArray)); + assertEquals(Byte.TYPE, newArray.getClass().getComponentType()); + byte[] array1 = new byte[]{1, 2, 3}; + newArray = ArrayUtils.add(array1, (byte)0); + assertTrue(Arrays.equals(new byte[]{1, 2, 3, 0}, newArray)); + assertEquals(Byte.TYPE, newArray.getClass().getComponentType()); + newArray = ArrayUtils.add(array1, (byte)4); + assertTrue(Arrays.equals(new byte[]{1, 2, 3, 4}, newArray)); + assertEquals(Byte.TYPE, newArray.getClass().getComponentType()); + } + + @Test + public void testAddObjectArrayChar() { + char[] newArray; + newArray = ArrayUtils.add((char[])null, (char)0); + assertTrue(Arrays.equals(new char[]{0}, newArray)); + assertEquals(Character.TYPE, newArray.getClass().getComponentType()); + newArray = ArrayUtils.add((char[])null, (char)1); + assertTrue(Arrays.equals(new char[]{1}, newArray)); + assertEquals(Character.TYPE, newArray.getClass().getComponentType()); + char[] array1 = new char[]{1, 2, 3}; + newArray = ArrayUtils.add(array1, (char)0); + assertTrue(Arrays.equals(new char[]{1, 2, 3, 0}, newArray)); + assertEquals(Character.TYPE, newArray.getClass().getComponentType()); + newArray = ArrayUtils.add(array1, (char)4); + assertTrue(Arrays.equals(new char[]{1, 2, 3, 4}, newArray)); + assertEquals(Character.TYPE, newArray.getClass().getComponentType()); + } + + @Test + public void testAddObjectArrayDouble() { + double[] newArray; + newArray = ArrayUtils.add((double[])null, 0); + assertTrue(Arrays.equals(new double[]{0}, newArray)); + assertEquals(Double.TYPE, newArray.getClass().getComponentType()); + newArray = ArrayUtils.add((double[])null, 1); + assertTrue(Arrays.equals(new double[]{1}, newArray)); + assertEquals(Double.TYPE, newArray.getClass().getComponentType()); + double[] array1 = new double[]{1, 2, 3}; + newArray = ArrayUtils.add(array1, 0); + assertTrue(Arrays.equals(new double[]{1, 2, 3, 0}, newArray)); + assertEquals(Double.TYPE, newArray.getClass().getComponentType()); + newArray = ArrayUtils.add(array1, 4); + assertTrue(Arrays.equals(new double[]{1, 2, 3, 4}, newArray)); + assertEquals(Double.TYPE, newArray.getClass().getComponentType()); + } + + @Test + public void testAddObjectArrayFloat() { + float[] newArray; + newArray = ArrayUtils.add((float[])null, 0); + assertTrue(Arrays.equals(new float[]{0}, newArray)); + assertEquals(Float.TYPE, newArray.getClass().getComponentType()); + newArray = ArrayUtils.add((float[])null, 1); + assertTrue(Arrays.equals(new float[]{1}, newArray)); + assertEquals(Float.TYPE, newArray.getClass().getComponentType()); + float[] array1 = new float[]{1, 2, 3}; + newArray = ArrayUtils.add(array1, 0); + assertTrue(Arrays.equals(new float[]{1, 2, 3, 0}, newArray)); + assertEquals(Float.TYPE, newArray.getClass().getComponentType()); + newArray = ArrayUtils.add(array1, 4); + assertTrue(Arrays.equals(new float[]{1, 2, 3, 4}, newArray)); + assertEquals(Float.TYPE, newArray.getClass().getComponentType()); + } + + @Test + public void testAddObjectArrayInt() { + int[] newArray; + newArray = ArrayUtils.add((int[])null, 0); + assertTrue(Arrays.equals(new int[]{0}, newArray)); + assertEquals(Integer.TYPE, newArray.getClass().getComponentType()); + newArray = ArrayUtils.add((int[])null, 1); + assertTrue(Arrays.equals(new int[]{1}, newArray)); + assertEquals(Integer.TYPE, newArray.getClass().getComponentType()); + int[] array1 = new int[]{1, 2, 3}; + newArray = ArrayUtils.add(array1, 0); + assertTrue(Arrays.equals(new int[]{1, 2, 3, 0}, newArray)); + assertEquals(Integer.TYPE, newArray.getClass().getComponentType()); + newArray = ArrayUtils.add(array1, 4); + assertTrue(Arrays.equals(new int[]{1, 2, 3, 4}, newArray)); + assertEquals(Integer.TYPE, newArray.getClass().getComponentType()); + } + + @Test + public void testAddObjectArrayLong() { + long[] newArray; + newArray = ArrayUtils.add((long[])null, 0); + assertTrue(Arrays.equals(new long[]{0}, newArray)); + assertEquals(Long.TYPE, newArray.getClass().getComponentType()); + newArray = ArrayUtils.add((long[])null, 1); + assertTrue(Arrays.equals(new long[]{1}, newArray)); + assertEquals(Long.TYPE, newArray.getClass().getComponentType()); + long[] array1 = new long[]{1, 2, 3}; + newArray = ArrayUtils.add(array1, 0); + assertTrue(Arrays.equals(new long[]{1, 2, 3, 0}, newArray)); + assertEquals(Long.TYPE, newArray.getClass().getComponentType()); + newArray = ArrayUtils.add(array1, 4); + assertTrue(Arrays.equals(new long[]{1, 2, 3, 4}, newArray)); + assertEquals(Long.TYPE, newArray.getClass().getComponentType()); + } + + @Test + public void testAddObjectArrayShort() { + short[] newArray; + newArray = ArrayUtils.add((short[])null, (short)0); + assertTrue(Arrays.equals(new short[]{0}, newArray)); + assertEquals(Short.TYPE, newArray.getClass().getComponentType()); + newArray = ArrayUtils.add((short[])null, (short)1); + assertTrue(Arrays.equals(new short[]{1}, newArray)); + assertEquals(Short.TYPE, newArray.getClass().getComponentType()); + short[] array1 = new short[]{1, 2, 3}; + newArray = ArrayUtils.add(array1, (short)0); + assertTrue(Arrays.equals(new short[]{1, 2, 3, 0}, newArray)); + assertEquals(Short.TYPE, newArray.getClass().getComponentType()); + newArray = ArrayUtils.add(array1, (short)4); + assertTrue(Arrays.equals(new short[]{1, 2, 3, 4}, newArray)); + assertEquals(Short.TYPE, newArray.getClass().getComponentType()); + } + + @Test + public void testAddObjectArrayObject() { + Object[] newArray; + + //show that not casting is okay + newArray = ArrayUtils.add((Object[])null, "a"); + assertTrue(Arrays.equals(new String[]{"a"}, newArray)); + assertTrue(Arrays.equals(new Object[]{"a"}, newArray)); + assertEquals(String.class, newArray.getClass().getComponentType()); + + //show that not casting to Object[] is okay and will assume String based on "a" + String[] newStringArray = ArrayUtils.add(null, "a"); + assertTrue(Arrays.equals(new String[]{"a"}, newStringArray)); + assertTrue(Arrays.equals(new Object[]{"a"}, newStringArray)); + assertEquals(String.class, newStringArray.getClass().getComponentType()); + + String[] stringArray1 = new String[]{"a", "b", "c"}; + newArray = ArrayUtils.add(stringArray1, null); + assertTrue(Arrays.equals(new String[]{"a", "b", "c", null}, newArray)); + assertEquals(String.class, newArray.getClass().getComponentType()); + + newArray = ArrayUtils.add(stringArray1, "d"); + assertTrue(Arrays.equals(new String[]{"a", "b", "c", "d"}, newArray)); + assertEquals(String.class, newArray.getClass().getComponentType()); + + Number[] numberArray1 = new Number[]{Integer.valueOf(1), Double.valueOf(2)}; + newArray = ArrayUtils.add(numberArray1, Float.valueOf(3)); + assertTrue(Arrays.equals(new Number[]{Integer.valueOf(1), Double.valueOf(2), Float.valueOf(3)}, newArray)); + assertEquals(Number.class, newArray.getClass().getComponentType()); + + numberArray1 = null; + newArray = ArrayUtils.add(numberArray1, Float.valueOf(3)); + assertTrue(Arrays.equals(new Float[]{Float.valueOf(3)}, newArray)); + assertEquals(Float.class, newArray.getClass().getComponentType()); + } + + @Test + public void testLANG571(){ + String[] stringArray=null; + String aString=null; + try { + @SuppressWarnings("unused") + String[] sa = ArrayUtils.add(stringArray, aString); + fail("Should have caused IllegalArgumentException"); + } catch (IllegalArgumentException iae){ + //expected + } + try { + @SuppressWarnings("unused") + String[] sa = ArrayUtils.add(stringArray, 0, aString); + fail("Should have caused IllegalArgumentException"); + } catch (IllegalArgumentException iae){ + //expected + } + } + + @Test + public void testAddObjectArrayToObjectArray() { + assertNull(ArrayUtils.addAll((Object[]) null, (Object[]) null)); + Object[] newArray; + String[] stringArray1 = new String[]{"a", "b", "c"}; + String[] stringArray2 = new String[]{"1", "2", "3"}; + newArray = ArrayUtils.addAll(stringArray1, (String[]) null); + assertNotSame(stringArray1, newArray); + assertTrue(Arrays.equals(stringArray1, newArray)); + assertTrue(Arrays.equals(new String[]{"a", "b", "c"}, newArray)); + assertEquals(String.class, newArray.getClass().getComponentType()); + newArray = ArrayUtils.addAll(null, stringArray2); + assertNotSame(stringArray2, newArray); + assertTrue(Arrays.equals(stringArray2, newArray)); + assertTrue(Arrays.equals(new String[]{"1", "2", "3"}, newArray)); + assertEquals(String.class, newArray.getClass().getComponentType()); + newArray = ArrayUtils.addAll(stringArray1, stringArray2); + assertTrue(Arrays.equals(new String[]{"a", "b", "c", "1", "2", "3"}, newArray)); + assertEquals(String.class, newArray.getClass().getComponentType()); + newArray = ArrayUtils.addAll(ArrayUtils.EMPTY_STRING_ARRAY, (String[]) null); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_STRING_ARRAY, newArray)); + assertTrue(Arrays.equals(new String[]{}, newArray)); + assertEquals(String.class, newArray.getClass().getComponentType()); + newArray = ArrayUtils.addAll(null, ArrayUtils.EMPTY_STRING_ARRAY); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_STRING_ARRAY, newArray)); + assertTrue(Arrays.equals(new String[]{}, newArray)); + assertEquals(String.class, newArray.getClass().getComponentType()); + newArray = ArrayUtils.addAll(ArrayUtils.EMPTY_STRING_ARRAY, ArrayUtils.EMPTY_STRING_ARRAY); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_STRING_ARRAY, newArray)); + assertTrue(Arrays.equals(new String[]{}, newArray)); + assertEquals(String.class, newArray.getClass().getComponentType()); + String[] stringArrayNull = new String []{null}; + newArray = ArrayUtils.addAll(stringArrayNull, stringArrayNull); + assertTrue(Arrays.equals(new String[]{null, null}, newArray)); + assertEquals(String.class, newArray.getClass().getComponentType()); + + // boolean + assertTrue( Arrays.equals( new boolean[] { true, false, false, true }, + ArrayUtils.addAll( new boolean[] { true, false }, new boolean[] { false, true } ) ) ); + + assertTrue( Arrays.equals( new boolean[] { false, true }, + ArrayUtils.addAll( null, new boolean[] { false, true } ) ) ); + + assertTrue( Arrays.equals( new boolean[] { true, false }, + ArrayUtils.addAll( new boolean[] { true, false }, null ) ) ); + + // char + assertTrue( Arrays.equals( new char[] { 'a', 'b', 'c', 'd' }, + ArrayUtils.addAll( new char[] { 'a', 'b' }, new char[] { 'c', 'd' } ) ) ); + + assertTrue( Arrays.equals( new char[] { 'c', 'd' }, + ArrayUtils.addAll( null, new char[] { 'c', 'd' } ) ) ); + + assertTrue( Arrays.equals( new char[] { 'a', 'b' }, + ArrayUtils.addAll( new char[] { 'a', 'b' }, null ) ) ); + + // byte + assertTrue( Arrays.equals( new byte[] { (byte) 0, (byte) 1, (byte) 2, (byte) 3 }, + ArrayUtils.addAll( new byte[] { (byte) 0, (byte) 1 }, new byte[] { (byte) 2, (byte) 3 } ) ) ); + + assertTrue( Arrays.equals( new byte[] { (byte) 2, (byte) 3 }, + ArrayUtils.addAll( null, new byte[] { (byte) 2, (byte) 3 } ) ) ); + + assertTrue( Arrays.equals( new byte[] { (byte) 0, (byte) 1 }, + ArrayUtils.addAll( new byte[] { (byte) 0, (byte) 1 }, null ) ) ); + + // short + assertTrue( Arrays.equals( new short[] { (short) 10, (short) 20, (short) 30, (short) 40 }, + ArrayUtils.addAll( new short[] { (short) 10, (short) 20 }, new short[] { (short) 30, (short) 40 } ) ) ); + + assertTrue( Arrays.equals( new short[] { (short) 30, (short) 40 }, + ArrayUtils.addAll( null, new short[] { (short) 30, (short) 40 } ) ) ); + + assertTrue( Arrays.equals( new short[] { (short) 10, (short) 20 }, + ArrayUtils.addAll( new short[] { (short) 10, (short) 20 }, null ) ) ); + + // int + assertTrue( Arrays.equals( new int[] { 1, 1000, -1000, -1 }, + ArrayUtils.addAll( new int[] { 1, 1000 }, new int[] { -1000, -1 } ) ) ); + + assertTrue( Arrays.equals( new int[] { -1000, -1 }, + ArrayUtils.addAll( null, new int[] { -1000, -1 } ) ) ); + + assertTrue( Arrays.equals( new int[] { 1, 1000 }, + ArrayUtils.addAll( new int[] { 1, 1000 }, null ) ) ); + + // long + assertTrue( Arrays.equals( new long[] { 1L, -1L, 1000L, -1000L }, + ArrayUtils.addAll( new long[] { 1L, -1L }, new long[] { 1000L, -1000L } ) ) ); + + assertTrue( Arrays.equals( new long[] { 1000L, -1000L }, + ArrayUtils.addAll( null, new long[] { 1000L, -1000L } ) ) ); + + assertTrue( Arrays.equals( new long[] { 1L, -1L }, + ArrayUtils.addAll( new long[] { 1L, -1L }, null ) ) ); + + // float + assertTrue( Arrays.equals( new float[] { 10.5f, 10.1f, 1.6f, 0.01f }, + ArrayUtils.addAll( new float[] { 10.5f, 10.1f }, new float[] { 1.6f, 0.01f } ) ) ); + + assertTrue( Arrays.equals( new float[] { 1.6f, 0.01f }, + ArrayUtils.addAll( null, new float[] { 1.6f, 0.01f } ) ) ); + + assertTrue( Arrays.equals( new float[] { 10.5f, 10.1f }, + ArrayUtils.addAll( new float[] { 10.5f, 10.1f }, null ) ) ); + + // double + assertTrue( Arrays.equals( new double[] { Math.PI, -Math.PI, 0, 9.99 }, + ArrayUtils.addAll( new double[] { Math.PI, -Math.PI }, new double[] { 0, 9.99 } ) ) ); + + assertTrue( Arrays.equals( new double[] { 0, 9.99 }, + ArrayUtils.addAll( null, new double[] { 0, 9.99 } ) ) ); + + assertTrue( Arrays.equals( new double[] { Math.PI, -Math.PI }, + ArrayUtils.addAll( new double[] { Math.PI, -Math.PI }, null ) ) ); + + } + + @Test + public void testAddObjectAtIndex() { + Object[] newArray; + newArray = ArrayUtils.add((Object[])null, 0, "a"); + assertTrue(Arrays.equals(new String[]{"a"}, newArray)); + assertTrue(Arrays.equals(new Object[]{"a"}, newArray)); + assertEquals(String.class, newArray.getClass().getComponentType()); + String[] stringArray1 = new String[]{"a", "b", "c"}; + newArray = ArrayUtils.add(stringArray1, 0, null); + assertTrue(Arrays.equals(new String[]{null, "a", "b", "c"}, newArray)); + assertEquals(String.class, newArray.getClass().getComponentType()); + newArray = ArrayUtils.add(stringArray1, 1, null); + assertTrue(Arrays.equals(new String[]{"a", null, "b", "c"}, newArray)); + assertEquals(String.class, newArray.getClass().getComponentType()); + newArray = ArrayUtils.add(stringArray1, 3, null); + assertTrue(Arrays.equals(new String[]{"a", "b", "c", null}, newArray)); + assertEquals(String.class, newArray.getClass().getComponentType()); + newArray = ArrayUtils.add(stringArray1, 3, "d"); + assertTrue(Arrays.equals(new String[]{"a", "b", "c", "d"}, newArray)); + assertEquals(String.class, newArray.getClass().getComponentType()); + assertEquals(String.class, newArray.getClass().getComponentType()); + + Object[] o = new Object[] {"1", "2", "4"}; + Object[] result = ArrayUtils.add(o, 2, "3"); + Object[] result2 = ArrayUtils.add(o, 3, "5"); + + assertNotNull(result); + assertEquals(4, result.length); + assertEquals("1", result[0]); + assertEquals("2", result[1]); + assertEquals("3", result[2]); + assertEquals("4", result[3]); + assertNotNull(result2); + assertEquals(4, result2.length); + assertEquals("1", result2[0]); + assertEquals("2", result2[1]); + assertEquals("4", result2[2]); + assertEquals("5", result2[3]); + + // boolean tests + boolean[] booleanArray = ArrayUtils.add( null, 0, true ); + assertTrue( Arrays.equals( new boolean[] { true }, booleanArray ) ); + try { + booleanArray = ArrayUtils.add( null, -1, true ); + } catch(IndexOutOfBoundsException e) { + assertEquals("Index: -1, Length: 0", e.getMessage()); + } + booleanArray = ArrayUtils.add( new boolean[] { true }, 0, false); + assertTrue( Arrays.equals( new boolean[] { false, true }, booleanArray ) ); + booleanArray = ArrayUtils.add( new boolean[] { false }, 1, true); + assertTrue( Arrays.equals( new boolean[] { false, true }, booleanArray ) ); + booleanArray = ArrayUtils.add( new boolean[] { true, false }, 1, true); + assertTrue( Arrays.equals( new boolean[] { true, true, false }, booleanArray ) ); + try { + booleanArray = ArrayUtils.add( new boolean[] { true, false }, 4, true); + } catch(IndexOutOfBoundsException e) { + assertEquals("Index: 4, Length: 2", e.getMessage()); + } + try { + booleanArray = ArrayUtils.add( new boolean[] { true, false }, -1, true); + } catch(IndexOutOfBoundsException e) { + assertEquals("Index: -1, Length: 2", e.getMessage()); + } + + // char tests + char[] charArray = ArrayUtils.add( (char[]) null, 0, 'a' ); + assertTrue( Arrays.equals( new char[] { 'a' }, charArray ) ); + try { + charArray = ArrayUtils.add( (char[]) null, -1, 'a' ); + } catch(IndexOutOfBoundsException e) { + assertEquals("Index: -1, Length: 0", e.getMessage()); + } + charArray = ArrayUtils.add( new char[] { 'a' }, 0, 'b'); + assertTrue( Arrays.equals( new char[] { 'b', 'a' }, charArray ) ); + charArray = ArrayUtils.add( new char[] { 'a', 'b' }, 0, 'c'); + assertTrue( Arrays.equals( new char[] { 'c', 'a', 'b' }, charArray ) ); + charArray = ArrayUtils.add( new char[] { 'a', 'b' }, 1, 'k'); + assertTrue( Arrays.equals( new char[] { 'a', 'k', 'b' }, charArray ) ); + charArray = ArrayUtils.add( new char[] { 'a', 'b', 'c' }, 1, 't'); + assertTrue( Arrays.equals( new char[] { 'a', 't', 'b', 'c' }, charArray ) ); + try { + charArray = ArrayUtils.add( new char[] { 'a', 'b' }, 4, 'c'); + } catch(IndexOutOfBoundsException e) { + assertEquals("Index: 4, Length: 2", e.getMessage()); + } + try { + charArray = ArrayUtils.add( new char[] { 'a', 'b' }, -1, 'c'); + } catch(IndexOutOfBoundsException e) { + assertEquals("Index: -1, Length: 2", e.getMessage()); + } + + // short tests + short[] shortArray = ArrayUtils.add( new short[] { 1 }, 0, (short) 2); + assertTrue( Arrays.equals( new short[] { 2, 1 }, shortArray ) ); + try { + shortArray = ArrayUtils.add( (short[]) null, -1, (short) 2); + } catch(IndexOutOfBoundsException e) { + assertEquals("Index: -1, Length: 0", e.getMessage()); + } + shortArray = ArrayUtils.add( new short[] { 2, 6 }, 2, (short) 10); + assertTrue( Arrays.equals( new short[] { 2, 6, 10 }, shortArray ) ); + shortArray = ArrayUtils.add( new short[] { 2, 6 }, 0, (short) -4); + assertTrue( Arrays.equals( new short[] { -4, 2, 6 }, shortArray ) ); + shortArray = ArrayUtils.add( new short[] { 2, 6, 3 }, 2, (short) 1); + assertTrue( Arrays.equals( new short[] { 2, 6, 1, 3 }, shortArray ) ); + try { + shortArray = ArrayUtils.add( new short[] { 2, 6 }, 4, (short) 10); + } catch(IndexOutOfBoundsException e) { + assertEquals("Index: 4, Length: 2", e.getMessage()); + } + try { + shortArray = ArrayUtils.add( new short[] { 2, 6 }, -1, (short) 10); + } catch(IndexOutOfBoundsException e) { + assertEquals("Index: -1, Length: 2", e.getMessage()); + } + + // byte tests + byte[] byteArray = ArrayUtils.add( new byte[] { 1 }, 0, (byte) 2); + assertTrue( Arrays.equals( new byte[] { 2, 1 }, byteArray ) ); + try { + byteArray = ArrayUtils.add( (byte[]) null, -1, (byte) 2); + } catch(IndexOutOfBoundsException e) { + assertEquals("Index: -1, Length: 0", e.getMessage()); + } + byteArray = ArrayUtils.add( new byte[] { 2, 6 }, 2, (byte) 3); + assertTrue( Arrays.equals( new byte[] { 2, 6, 3 }, byteArray ) ); + byteArray = ArrayUtils.add( new byte[] { 2, 6 }, 0, (byte) 1); + assertTrue( Arrays.equals( new byte[] { 1, 2, 6 }, byteArray ) ); + byteArray = ArrayUtils.add( new byte[] { 2, 6, 3 }, 2, (byte) 1); + assertTrue( Arrays.equals( new byte[] { 2, 6, 1, 3 }, byteArray ) ); + try { + byteArray = ArrayUtils.add( new byte[] { 2, 6 }, 4, (byte) 3); + } catch(IndexOutOfBoundsException e) { + assertEquals("Index: 4, Length: 2", e.getMessage()); + } + try { + byteArray = ArrayUtils.add( new byte[] { 2, 6 }, -1, (byte) 3); + } catch(IndexOutOfBoundsException e) { + assertEquals("Index: -1, Length: 2", e.getMessage()); + } + + // int tests + int[] intArray = ArrayUtils.add( new int[] { 1 }, 0, 2); + assertTrue( Arrays.equals( new int[] { 2, 1 }, intArray ) ); + try { + intArray = ArrayUtils.add( (int[]) null, -1, 2); + } catch(IndexOutOfBoundsException e) { + assertEquals("Index: -1, Length: 0", e.getMessage()); + } + intArray = ArrayUtils.add( new int[] { 2, 6 }, 2, 10); + assertTrue( Arrays.equals( new int[] { 2, 6, 10 }, intArray ) ); + intArray = ArrayUtils.add( new int[] { 2, 6 }, 0, -4); + assertTrue( Arrays.equals( new int[] { -4, 2, 6 }, intArray ) ); + intArray = ArrayUtils.add( new int[] { 2, 6, 3 }, 2, 1); + assertTrue( Arrays.equals( new int[] { 2, 6, 1, 3 }, intArray ) ); + try { + intArray = ArrayUtils.add( new int[] { 2, 6 }, 4, 10); + } catch(IndexOutOfBoundsException e) { + assertEquals("Index: 4, Length: 2", e.getMessage()); + } + try { + intArray = ArrayUtils.add( new int[] { 2, 6 }, -1, 10); + } catch(IndexOutOfBoundsException e) { + assertEquals("Index: -1, Length: 2", e.getMessage()); + } + + // long tests + long[] longArray = ArrayUtils.add( new long[] { 1L }, 0, 2L); + assertTrue( Arrays.equals( new long[] { 2L, 1L }, longArray ) ); + try { + longArray = ArrayUtils.add( (long[]) null, -1, 2L); + } catch(IndexOutOfBoundsException e) { + assertEquals("Index: -1, Length: 0", e.getMessage()); + } + longArray = ArrayUtils.add( new long[] { 2L, 6L }, 2, 10L); + assertTrue( Arrays.equals( new long[] { 2L, 6L, 10L }, longArray ) ); + longArray = ArrayUtils.add( new long[] { 2L, 6L }, 0, -4L); + assertTrue( Arrays.equals( new long[] { -4L, 2L, 6L }, longArray ) ); + longArray = ArrayUtils.add( new long[] { 2L, 6L, 3L }, 2, 1L); + assertTrue( Arrays.equals( new long[] { 2L, 6L, 1L, 3L }, longArray ) ); + try { + longArray = ArrayUtils.add( new long[] { 2L, 6L }, 4, 10L); + } catch(IndexOutOfBoundsException e) { + assertEquals("Index: 4, Length: 2", e.getMessage()); + } + try { + longArray = ArrayUtils.add( new long[] { 2L, 6L }, -1, 10L); + } catch(IndexOutOfBoundsException e) { + assertEquals("Index: -1, Length: 2", e.getMessage()); + } + + // float tests + float[] floatArray = ArrayUtils.add( new float[] { 1.1f }, 0, 2.2f); + assertTrue( Arrays.equals( new float[] { 2.2f, 1.1f }, floatArray ) ); + try { + floatArray = ArrayUtils.add( (float[]) null, -1, 2.2f); + } catch(IndexOutOfBoundsException e) { + assertEquals("Index: -1, Length: 0", e.getMessage()); + } + floatArray = ArrayUtils.add( new float[] { 2.3f, 6.4f }, 2, 10.5f); + assertTrue( Arrays.equals( new float[] { 2.3f, 6.4f, 10.5f }, floatArray ) ); + floatArray = ArrayUtils.add( new float[] { 2.6f, 6.7f }, 0, -4.8f); + assertTrue( Arrays.equals( new float[] { -4.8f, 2.6f, 6.7f }, floatArray ) ); + floatArray = ArrayUtils.add( new float[] { 2.9f, 6.0f, 0.3f }, 2, 1.0f); + assertTrue( Arrays.equals( new float[] { 2.9f, 6.0f, 1.0f, 0.3f }, floatArray ) ); + try { + floatArray = ArrayUtils.add( new float[] { 2.3f, 6.4f }, 4, 10.5f); + } catch(IndexOutOfBoundsException e) { + assertEquals("Index: 4, Length: 2", e.getMessage()); + } + try { + floatArray = ArrayUtils.add( new float[] { 2.3f, 6.4f }, -1, 10.5f); + } catch(IndexOutOfBoundsException e) { + assertEquals("Index: -1, Length: 2", e.getMessage()); + } + + // double tests + double[] doubleArray = ArrayUtils.add( new double[] { 1.1 }, 0, 2.2); + assertTrue( Arrays.equals( new double[] { 2.2, 1.1 }, doubleArray ) ); + try { + doubleArray = ArrayUtils.add( (double[]) null, -1, 2.2); + } catch(IndexOutOfBoundsException e) { + assertEquals("Index: -1, Length: 0", e.getMessage()); + } + doubleArray = ArrayUtils.add( new double[] { 2.3, 6.4 }, 2, 10.5); + assertTrue( Arrays.equals( new double[] { 2.3, 6.4, 10.5 }, doubleArray ) ); + doubleArray = ArrayUtils.add( new double[] { 2.6, 6.7 }, 0, -4.8); + assertTrue( Arrays.equals( new double[] { -4.8, 2.6, 6.7 }, doubleArray ) ); + doubleArray = ArrayUtils.add( new double[] { 2.9, 6.0, 0.3 }, 2, 1.0); + assertTrue( Arrays.equals( new double[] { 2.9, 6.0, 1.0, 0.3 }, doubleArray ) ); + try { + doubleArray = ArrayUtils.add( new double[] { 2.3, 6.4 }, 4, 10.5); + } catch(IndexOutOfBoundsException e) { + assertEquals("Index: 4, Length: 2", e.getMessage()); + } + try { + doubleArray = ArrayUtils.add( new double[] { 2.3, 6.4 }, -1, 10.5); + } catch(IndexOutOfBoundsException e) { + assertEquals("Index: -1, Length: 2", e.getMessage()); + } + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/ArrayUtilsRemoveMultipleTest.java b/ApacheCommonsLang/org/apache/commons/lang3/ArrayUtilsRemoveMultipleTest.java new file mode 100644 index 0000000..8c26c13 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/ArrayUtilsRemoveMultipleTest.java @@ -0,0 +1,1049 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.lang3; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; + +import org.junit.Test; + +/** + * Tests ArrayUtils remove and removeElement methods. + * + * @version $Id: ArrayUtilsRemoveMultipleTest.java 1309910 2012-04-05 15:52:43Z ggregory $ + */ +public class ArrayUtilsRemoveMultipleTest { + + @Test + public void testRemoveAllObjectArray() { + Object[] array; + array = ArrayUtils.removeAll(new Object[] { "a" }, 0); + assertArrayEquals(ArrayUtils.EMPTY_OBJECT_ARRAY, array); + assertEquals(Object.class, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new Object[] { "a", "b" }, 0, 1); + assertArrayEquals(ArrayUtils.EMPTY_OBJECT_ARRAY, array); + assertEquals(Object.class, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new Object[] { "a", "b", "c" }, 1, 2); + assertArrayEquals(new Object[] { "a" }, array); + assertEquals(Object.class, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new Object[] { "a", "b", "c", "d" }, 1, 2); + assertArrayEquals(new Object[] { "a", "d" }, array); + assertEquals(Object.class, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new Object[] { "a", "b", "c", "d" }, 0, 3); + assertArrayEquals(new Object[] { "b", "c" }, array); + assertEquals(Object.class, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new Object[] { "a", "b", "c", "d" }, 0, 1, 3); + assertArrayEquals(new Object[] { "c" }, array); + assertEquals(Object.class, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new Object[] { "a", "b", "c", "d", "e" }, 0, 1, 3); + assertArrayEquals(new Object[] { "c", "e" }, array); + assertEquals(Object.class, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new Object[] { "a", "b", "c", "d", "e" }, 0, 2, 4); + assertArrayEquals(new Object[] { "b", "d" }, array); + assertEquals(Object.class, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new Object[] { "a", "b", "c", "d" }, 0, 1, 3, 0, 1, 3); + assertArrayEquals(new Object[] { "c" }, array); + assertEquals(Object.class, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new Object[] { "a", "b", "c", "d" }, 2, 1, 0, 3); + assertArrayEquals(ArrayUtils.EMPTY_OBJECT_ARRAY, array); + assertEquals(Object.class, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new Object[] { "a", "b", "c", "d" }, 2, 0, 1, 3, 0, 2, 1, 3); + assertArrayEquals(ArrayUtils.EMPTY_OBJECT_ARRAY, array); + assertEquals(Object.class, array.getClass().getComponentType()); + } + + @Test + public void testRemoveAllObjectArrayRemoveNone() { + Object[] array1 = new Object[] { "foo", "bar", "baz" }; + Object[] array2 = ArrayUtils.removeAll(array1); + assertNotSame(array1, array2); + assertArrayEquals(array1, array2); + assertEquals(Object.class, array2.getClass().getComponentType()); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testRemoveAllObjectArrayNegativeIndex() { + ArrayUtils.removeAll(new Object[] { "a", "b" }, -1); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testRemoveAllObjectArrayOutOfBoundsIndex() { + ArrayUtils.removeAll(new Object[] { "a", "b" }, 2); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testRemoveAllNullObjectArray() { + ArrayUtils.remove((Object[]) null, 0); + } + + @Test + public void testRemoveAllNumberArray() { + Number[] inarray = { Integer.valueOf(1), Long.valueOf(2L), Byte.valueOf((byte) 3) }; + assertEquals(3, inarray.length); + Number[] outarray; + outarray = ArrayUtils.removeAll(inarray, 1); + assertArrayEquals(new Number[] { Integer.valueOf(1), Byte.valueOf((byte) 3) }, outarray); + assertEquals(Number.class, outarray.getClass().getComponentType()); + outarray = ArrayUtils.removeAll(outarray, 1); + assertArrayEquals(new Number[] { Integer.valueOf(1) }, outarray); + assertEquals(Number.class, outarray.getClass().getComponentType()); + outarray = ArrayUtils.removeAll(outarray, 0); + assertEquals(0, outarray.length); + assertEquals(Number.class, outarray.getClass().getComponentType()); + + outarray = ArrayUtils.removeAll(inarray, 0, 1); + assertArrayEquals(new Number[] { Byte.valueOf((byte) 3) }, outarray); + assertEquals(Number.class, outarray.getClass().getComponentType()); + outarray = ArrayUtils.removeAll(inarray, 0, 2); + assertArrayEquals(new Number[] { Long.valueOf(2L) }, outarray); + assertEquals(Number.class, outarray.getClass().getComponentType()); + outarray = ArrayUtils.removeAll(inarray, 1, 2); + assertArrayEquals(new Number[] { Integer.valueOf(1) }, outarray); + assertEquals(Number.class, outarray.getClass().getComponentType()); + } + + @Test + public void testRemoveAllBooleanArray() { + boolean[] array; + array = ArrayUtils.removeAll(new boolean[] { true }, 0); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_BOOLEAN_ARRAY, array)); + assertEquals(Boolean.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new boolean[] { true, false }, 0); + assertTrue(Arrays.equals(new boolean[] { false }, array)); + assertEquals(Boolean.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new boolean[] { true, false }, 1); + assertTrue(Arrays.equals(new boolean[] { true }, array)); + assertEquals(Boolean.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new boolean[] { true, false, true }, 1); + assertTrue(Arrays.equals(new boolean[] { true, true }, array)); + assertEquals(Boolean.TYPE, array.getClass().getComponentType()); + + array = ArrayUtils.removeAll(new boolean[] { true, false }, 0, 1); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_BOOLEAN_ARRAY, array)); + assertEquals(Boolean.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new boolean[] { true, false, false }, 0, 1); + assertTrue(Arrays.equals(new boolean[] { false }, array)); + assertEquals(Boolean.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new boolean[] { true, false, false }, 0, 2); + assertTrue(Arrays.equals(new boolean[] { false }, array)); + assertEquals(Boolean.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new boolean[] { true, false, false }, 1, 2); + assertTrue(Arrays.equals(new boolean[] { true }, array)); + assertEquals(Boolean.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new boolean[] { true, false, true, false, true }, 0, 2, 4); + assertTrue(Arrays.equals(new boolean[] { false, false }, array)); + assertEquals(Boolean.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new boolean[] { true, false, true, false, true }, 1, 3); + assertTrue(Arrays.equals(new boolean[] { true, true, true }, array)); + assertEquals(Boolean.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new boolean[] { true, false, true, false, true }, 1, 3, 4); + assertTrue(Arrays.equals(new boolean[] { true, true }, array)); + assertEquals(Boolean.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new boolean[] { true, false, true, false, true, false, true }, 0, 2, 4, 6); + assertTrue(Arrays.equals(new boolean[] { false, false, false }, array)); + assertEquals(Boolean.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new boolean[] { true, false, true, false, true, false, true }, 1, 3, 5); + assertTrue(Arrays.equals(new boolean[] { true, true, true, true }, array)); + assertEquals(Boolean.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new boolean[] { true, false, true, false, true, false, true }, 0, 1, 2); + assertTrue(Arrays.equals(new boolean[] { false, true, false, true }, array)); + assertEquals(Boolean.TYPE, array.getClass().getComponentType()); + } + + @Test + public void testRemoveAllBooleanArrayRemoveNone() { + boolean[] array1 = new boolean[] { true, false }; + boolean[] array2 = ArrayUtils.removeAll(array1); + assertNotSame(array1, array2); + assertTrue(Arrays.equals(array1, array2)); + assertEquals(boolean.class, array2.getClass().getComponentType()); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testRemoveAllBooleanArrayNegativeIndex() { + ArrayUtils.removeAll(new boolean[] { true, false }, -1); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testRemoveAllBooleanArrayOutOfBoundsIndex() { + ArrayUtils.removeAll(new boolean[] { true, false }, 2); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testRemoveAllNullBooleanArray() { + ArrayUtils.removeAll((boolean[]) null, 0); + } + + @Test + public void testRemoveAllByteArray() { + byte[] array; + array = ArrayUtils.removeAll(new byte[] { 1 }, 0); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_BYTE_ARRAY, array)); + assertEquals(Byte.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new byte[] { 1, 2 }, 0); + assertTrue(Arrays.equals(new byte[] { 2 }, array)); + assertEquals(Byte.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new byte[] { 1, 2 }, 1); + assertTrue(Arrays.equals(new byte[] { 1 }, array)); + assertEquals(Byte.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new byte[] { 1, 2, 1 }, 1); + assertTrue(Arrays.equals(new byte[] { 1, 1 }, array)); + assertEquals(Byte.TYPE, array.getClass().getComponentType()); + + array = ArrayUtils.removeAll(new byte[] { 1, 2 }, 0, 1); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_BYTE_ARRAY, array)); + assertEquals(Byte.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new byte[] { 1, 2, 3 }, 0, 1); + assertTrue(Arrays.equals(new byte[] { 3 }, array)); + assertEquals(Byte.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new byte[] { 1, 2, 3 }, 1, 2); + assertTrue(Arrays.equals(new byte[] { 1 }, array)); + assertEquals(Byte.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new byte[] { 1, 2, 3 }, 0, 2); + assertTrue(Arrays.equals(new byte[] { 2 }, array)); + assertEquals(Byte.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new byte[] { 1, 2, 3, 4, 5 }, 1, 3); + assertTrue(Arrays.equals(new byte[] { 1, 3, 5 }, array)); + assertEquals(Byte.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new byte[] { 1, 2, 3, 4, 5 }, 0, 2, 4); + assertTrue(Arrays.equals(new byte[] { 2, 4 }, array)); + assertEquals(Byte.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new byte[] { 1, 2, 3, 4, 5, 6, 7 }, 1, 3, 5); + assertTrue(Arrays.equals(new byte[] { 1, 3, 5, 7 }, array)); + assertEquals(Byte.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new byte[] { 1, 2, 3, 4, 5, 6, 7 }, 0, 2, 4, 6); + assertTrue(Arrays.equals(new byte[] { 2, 4, 6 }, array)); + assertEquals(Byte.TYPE, array.getClass().getComponentType()); + } + + @Test + public void testRemoveAllByteArrayRemoveNone() { + byte[] array1 = new byte[] { 1, 2 }; + byte[] array2 = ArrayUtils.removeAll(array1); + assertNotSame(array1, array2); + assertArrayEquals(array1, array2); + assertEquals(byte.class, array2.getClass().getComponentType()); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testRemoveAllByteArrayNegativeIndex() { + ArrayUtils.removeAll(new byte[] { 1, 2 }, -1); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testRemoveAllByteArrayOutOfBoundsIndex() { + ArrayUtils.removeAll(new byte[] { 1, 2 }, 2); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testRemoveAllNullByteArray() { + ArrayUtils.removeAll((byte[]) null, 0); + } + + @Test + public void testRemoveAllCharArray() { + char[] array; + array = ArrayUtils.removeAll(new char[] { 'a' }, 0); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_CHAR_ARRAY, array)); + assertEquals(Character.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new char[] { 'a', 'b' }, 0); + assertTrue(Arrays.equals(new char[] { 'b' }, array)); + assertEquals(Character.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new char[] { 'a', 'b' }, 1); + assertTrue(Arrays.equals(new char[] { 'a' }, array)); + assertEquals(Character.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new char[] { 'a', 'b', 'c' }, 1); + assertTrue(Arrays.equals(new char[] { 'a', 'c' }, array)); + assertEquals(Character.TYPE, array.getClass().getComponentType()); + + array = ArrayUtils.removeAll(new char[] { 'a', 'b' }, 0, 1); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_CHAR_ARRAY, array)); + assertEquals(Character.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new char[] { 'a', 'b', 'c' }, 0, 1); + assertTrue(Arrays.equals(new char[] { 'c' }, array)); + assertEquals(Character.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new char[] { 'a', 'b', 'c' }, 1, 2); + assertTrue(Arrays.equals(new char[] { 'a' }, array)); + assertEquals(Character.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new char[] { 'a', 'b', 'c' }, 0, 2); + assertTrue(Arrays.equals(new char[] { 'b' }, array)); + assertEquals(Character.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new char[] { 'a', 'b', 'c', 'd', 'e' }, 1, 3); + assertTrue(Arrays.equals(new char[] { 'a', 'c', 'e' }, array)); + assertEquals(Character.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new char[] { 'a', 'b', 'c', 'd', 'e' }, 0, 2, 4); + assertTrue(Arrays.equals(new char[] { 'b', 'd' }, array)); + assertEquals(Character.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new char[] { 'a', 'b', 'c', 'd', 'e', 'f', 'g' }, 1, 3, 5); + assertTrue(Arrays.equals(new char[] { 'a', 'c', 'e', 'g' }, array)); + assertEquals(Character.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new char[] { 'a', 'b', 'c', 'd', 'e', 'f', 'g' }, 0, 2, 4, 6); + assertTrue(Arrays.equals(new char[] { 'b', 'd', 'f' }, array)); + assertEquals(Character.TYPE, array.getClass().getComponentType()); + } + + @Test + public void testRemoveAllCharArrayRemoveNone() { + char[] array1 = new char[] { 'a', 'b' }; + char[] array2 = ArrayUtils.removeAll(array1); + assertNotSame(array1, array2); + assertArrayEquals(array1, array2); + assertEquals(char.class, array2.getClass().getComponentType()); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testRemoveAllCharArrayNegativeIndex() { + ArrayUtils.removeAll(new char[] { 'a', 'b' }, -1); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testRemoveAllCharArrayOutOfBoundsIndex() { + ArrayUtils.removeAll(new char[] { 'a', 'b' }, 2); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testRemoveAllNullCharArray() { + ArrayUtils.removeAll((char[]) null, 0); + } + + @Test + public void testRemoveAllDoubleArray() { + double[] array; + array = ArrayUtils.removeAll(new double[] { 1 }, 0); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_DOUBLE_ARRAY, array)); + assertEquals(Double.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new double[] { 1, 2 }, 0); + assertTrue(Arrays.equals(new double[] { 2 }, array)); + assertEquals(Double.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new double[] { 1, 2 }, 1); + assertTrue(Arrays.equals(new double[] { 1 }, array)); + assertEquals(Double.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new double[] { 1, 2, 1 }, 1); + assertTrue(Arrays.equals(new double[] { 1, 1 }, array)); + assertEquals(Double.TYPE, array.getClass().getComponentType()); + + array = ArrayUtils.removeAll(new double[] { 1, 2 }, 0, 1); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_DOUBLE_ARRAY, array)); + assertEquals(Double.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new double[] { 1, 2, 3 }, 0, 1); + assertTrue(Arrays.equals(new double[] { 3 }, array)); + assertEquals(Double.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new double[] { 1, 2, 3 }, 1, 2); + assertTrue(Arrays.equals(new double[] { 1 }, array)); + assertEquals(Double.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new double[] { 1, 2, 3 }, 0, 2); + assertTrue(Arrays.equals(new double[] { 2 }, array)); + assertEquals(Double.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new double[] { 1, 2, 3, 4, 5 }, 1, 3); + assertTrue(Arrays.equals(new double[] { 1, 3, 5 }, array)); + assertEquals(Double.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new double[] { 1, 2, 3, 4, 5 }, 0, 2, 4); + assertTrue(Arrays.equals(new double[] { 2, 4 }, array)); + assertEquals(Double.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new double[] { 1, 2, 3, 4, 5, 6, 7 }, 1, 3, 5); + assertTrue(Arrays.equals(new double[] { 1, 3, 5, 7 }, array)); + assertEquals(Double.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new double[] { 1, 2, 3, 4, 5, 6, 7 }, 0, 2, 4, 6); + assertTrue(Arrays.equals(new double[] { 2, 4, 6 }, array)); + assertEquals(Double.TYPE, array.getClass().getComponentType()); + } + + @Test + public void testRemoveAllDoubleArrayRemoveNone() { + double[] array1 = new double[] { 1, 2 }; + double[] array2 = ArrayUtils.removeAll(array1); + assertNotSame(array1, array2); + assertTrue(Arrays.equals(array1, array2)); + assertEquals(double.class, array2.getClass().getComponentType()); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testRemoveAllDoubleArrayNegativeIndex() { + ArrayUtils.removeAll(new double[] { 1, 2 }, -1); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testRemoveAllDoubleArrayOutOfBoundsIndex() { + ArrayUtils.removeAll(new double[] { 1, 2 }, 2); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testRemoveAllNullDoubleArray() { + ArrayUtils.removeAll((double[]) null, 0); + } + + @Test + public void testRemoveAllFloatArray() { + float[] array; + array = ArrayUtils.removeAll(new float[] { 1 }, 0); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_FLOAT_ARRAY, array)); + assertEquals(Float.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new float[] { 1, 2 }, 0); + assertTrue(Arrays.equals(new float[] { 2 }, array)); + assertEquals(Float.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new float[] { 1, 2 }, 1); + assertTrue(Arrays.equals(new float[] { 1 }, array)); + assertEquals(Float.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new float[] { 1, 2, 1 }, 1); + assertTrue(Arrays.equals(new float[] { 1, 1 }, array)); + assertEquals(Float.TYPE, array.getClass().getComponentType()); + + array = ArrayUtils.removeAll(new float[] { 1, 2 }, 0, 1); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_FLOAT_ARRAY, array)); + assertEquals(Float.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new float[] { 1, 2, 3 }, 0, 1); + assertTrue(Arrays.equals(new float[] { 3 }, array)); + assertEquals(Float.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new float[] { 1, 2, 3 }, 1, 2); + assertTrue(Arrays.equals(new float[] { 1 }, array)); + assertEquals(Float.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new float[] { 1, 2, 3 }, 0, 2); + assertTrue(Arrays.equals(new float[] { 2 }, array)); + assertEquals(Float.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new float[] { 1, 2, 3, 4, 5 }, 1, 3); + assertTrue(Arrays.equals(new float[] { 1, 3, 5 }, array)); + assertEquals(Float.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new float[] { 1, 2, 3, 4, 5 }, 0, 2, 4); + assertTrue(Arrays.equals(new float[] { 2, 4 }, array)); + assertEquals(Float.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new float[] { 1, 2, 3, 4, 5, 6, 7 }, 1, 3, 5); + assertTrue(Arrays.equals(new float[] { 1, 3, 5, 7 }, array)); + assertEquals(Float.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new float[] { 1, 2, 3, 4, 5, 6, 7 }, 0, 2, 4, 6); + assertTrue(Arrays.equals(new float[] { 2, 4, 6 }, array)); + assertEquals(Float.TYPE, array.getClass().getComponentType()); + } + + @Test + public void testRemoveAllFloatArrayRemoveNone() { + float[] array1 = new float[] { 1, 2 }; + float[] array2 = ArrayUtils.removeAll(array1); + assertNotSame(array1, array2); + assertTrue(Arrays.equals(array1, array2)); + assertEquals(float.class, array2.getClass().getComponentType()); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testRemoveAllFloatArrayNegativeIndex() { + ArrayUtils.removeAll(new float[] { 1, 2 }, -1); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testRemoveAllFloatArrayOutOfBoundsIndex() { + ArrayUtils.removeAll(new float[] { 1, 2 }, 2); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testRemoveAllNullFloatArray() { + ArrayUtils.removeAll((float[]) null, 0); + } + + @Test + public void testRemoveAllIntArray() { + int[] array; + array = ArrayUtils.removeAll(new int[] { 1 }, 0); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_INT_ARRAY, array)); + assertEquals(Integer.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new int[] { 1, 2 }, 0); + assertTrue(Arrays.equals(new int[] { 2 }, array)); + assertEquals(Integer.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new int[] { 1, 2 }, 1); + assertTrue(Arrays.equals(new int[] { 1 }, array)); + assertEquals(Integer.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new int[] { 1, 2, 1 }, 1); + assertTrue(Arrays.equals(new int[] { 1, 1 }, array)); + assertEquals(Integer.TYPE, array.getClass().getComponentType()); + + array = ArrayUtils.removeAll(new int[] { 1, 2 }, 0, 1); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_INT_ARRAY, array)); + assertEquals(Integer.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new int[] { 1, 2, 3 }, 0, 1); + assertTrue(Arrays.equals(new int[] { 3 }, array)); + assertEquals(Integer.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new int[] { 1, 2, 3 }, 1, 2); + assertTrue(Arrays.equals(new int[] { 1 }, array)); + assertEquals(Integer.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new int[] { 1, 2, 3 }, 0, 2); + assertTrue(Arrays.equals(new int[] { 2 }, array)); + assertEquals(Integer.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new int[] { 1, 2, 3, 4, 5 }, 1, 3); + assertTrue(Arrays.equals(new int[] { 1, 3, 5 }, array)); + assertEquals(Integer.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new int[] { 1, 2, 3, 4, 5 }, 0, 2, 4); + assertTrue(Arrays.equals(new int[] { 2, 4 }, array)); + assertEquals(Integer.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new int[] { 1, 2, 3, 4, 5, 6, 7 }, 1, 3, 5); + assertTrue(Arrays.equals(new int[] { 1, 3, 5, 7 }, array)); + assertEquals(Integer.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new int[] { 1, 2, 3, 4, 5, 6, 7 }, 0, 2, 4, 6); + assertTrue(Arrays.equals(new int[] { 2, 4, 6 }, array)); + assertEquals(Integer.TYPE, array.getClass().getComponentType()); + } + + @Test + public void testRemoveAllIntArrayRemoveNone() { + int[] array1 = new int[] { 1, 2 }; + int[] array2 = ArrayUtils.removeAll(array1); + assertNotSame(array1, array2); + assertArrayEquals(array1, array2); + assertEquals(int.class, array2.getClass().getComponentType()); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testRemoveAllIntArrayNegativeIndex() { + ArrayUtils.removeAll(new int[] { 1, 2 }, -1); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testRemoveAllIntArrayOutOfBoundsIndex() { + ArrayUtils.removeAll(new int[] { 1, 2 }, 2); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testRemoveAllNullIntArray() { + ArrayUtils.removeAll((int[]) null, 0); + } + + @Test + public void testRemoveAllLongArray() { + long[] array; + array = ArrayUtils.removeAll(new long[] { 1 }, 0); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_LONG_ARRAY, array)); + assertEquals(Long.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new long[] { 1, 2 }, 0); + assertTrue(Arrays.equals(new long[] { 2 }, array)); + assertEquals(Long.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new long[] { 1, 2 }, 1); + assertTrue(Arrays.equals(new long[] { 1 }, array)); + assertEquals(Long.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new long[] { 1, 2, 1 }, 1); + assertTrue(Arrays.equals(new long[] { 1, 1 }, array)); + assertEquals(Long.TYPE, array.getClass().getComponentType()); + + array = ArrayUtils.removeAll(new long[] { 1, 2 }, 0, 1); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_LONG_ARRAY, array)); + assertEquals(Long.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new long[] { 1, 2, 3 }, 0, 1); + assertTrue(Arrays.equals(new long[] { 3 }, array)); + assertEquals(Long.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new long[] { 1, 2, 3 }, 1, 2); + assertTrue(Arrays.equals(new long[] { 1 }, array)); + assertEquals(Long.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new long[] { 1, 2, 3 }, 0, 2); + assertTrue(Arrays.equals(new long[] { 2 }, array)); + assertEquals(Long.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new long[] { 1, 2, 3, 4, 5 }, 1, 3); + assertTrue(Arrays.equals(new long[] { 1, 3, 5 }, array)); + assertEquals(Long.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new long[] { 1, 2, 3, 4, 5 }, 0, 2, 4); + assertTrue(Arrays.equals(new long[] { 2, 4 }, array)); + assertEquals(Long.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new long[] { 1, 2, 3, 4, 5, 6, 7 }, 1, 3, 5); + assertTrue(Arrays.equals(new long[] { 1, 3, 5, 7 }, array)); + assertEquals(Long.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new long[] { 1, 2, 3, 4, 5, 6, 7 }, 0, 2, 4, 6); + assertTrue(Arrays.equals(new long[] { 2, 4, 6 }, array)); + assertEquals(Long.TYPE, array.getClass().getComponentType()); + } + + @Test + public void testRemoveAllLongArrayRemoveNone() { + long[] array1 = new long[] { 1, 2 }; + long[] array2 = ArrayUtils.removeAll(array1); + assertNotSame(array1, array2); + assertArrayEquals(array1, array2); + assertEquals(long.class, array2.getClass().getComponentType()); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testRemoveAllLongArrayNegativeIndex() { + ArrayUtils.removeAll(new long[] { 1, 2 }, -1); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testRemoveAllLongArrayOutOfBoundsIndex() { + ArrayUtils.removeAll(new long[] { 1, 2 }, 2); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testRemoveAllNullLongArray() { + ArrayUtils.removeAll((long[]) null, 0); + } + + @Test + public void testRemoveAllShortArray() { + short[] array; + array = ArrayUtils.removeAll(new short[] { 1 }, 0); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_SHORT_ARRAY, array)); + assertEquals(Short.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new short[] { 1, 2 }, 0); + assertTrue(Arrays.equals(new short[] { 2 }, array)); + assertEquals(Short.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new short[] { 1, 2 }, 1); + assertTrue(Arrays.equals(new short[] { 1 }, array)); + assertEquals(Short.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new short[] { 1, 2, 1 }, 1); + assertTrue(Arrays.equals(new short[] { 1, 1 }, array)); + assertEquals(Short.TYPE, array.getClass().getComponentType()); + + array = ArrayUtils.removeAll(new short[] { 1, 2 }, 0, 1); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_SHORT_ARRAY, array)); + assertEquals(Short.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new short[] { 1, 2, 3 }, 0, 1); + assertTrue(Arrays.equals(new short[] { 3 }, array)); + assertEquals(Short.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new short[] { 1, 2, 3 }, 1, 2); + assertTrue(Arrays.equals(new short[] { 1 }, array)); + assertEquals(Short.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new short[] { 1, 2, 3 }, 0, 2); + assertTrue(Arrays.equals(new short[] { 2 }, array)); + assertEquals(Short.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new short[] { 1, 2, 3, 4, 5 }, 1, 3); + assertTrue(Arrays.equals(new short[] { 1, 3, 5 }, array)); + assertEquals(Short.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new short[] { 1, 2, 3, 4, 5 }, 0, 2, 4); + assertTrue(Arrays.equals(new short[] { 2, 4 }, array)); + assertEquals(Short.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new short[] { 1, 2, 3, 4, 5, 6, 7 }, 1, 3, 5); + assertTrue(Arrays.equals(new short[] { 1, 3, 5, 7 }, array)); + assertEquals(Short.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeAll(new short[] { 1, 2, 3, 4, 5, 6, 7 }, 0, 2, 4, 6); + assertTrue(Arrays.equals(new short[] { 2, 4, 6 }, array)); + assertEquals(Short.TYPE, array.getClass().getComponentType()); + } + + @Test + public void testRemoveAllShortArrayRemoveNone() { + short[] array1 = new short[] { 1, 2 }; + short[] array2 = ArrayUtils.removeAll(array1); + assertNotSame(array1, array2); + assertArrayEquals(array1, array2); + assertEquals(short.class, array2.getClass().getComponentType()); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testRemoveAllShortArrayNegativeIndex() { + ArrayUtils.removeAll(new short[] { 1, 2 }, -1, 0); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testRemoveAllShortArrayOutOfBoundsIndex() { + ArrayUtils.removeAll(new short[] { 1, 2 }, 2, 0); + } + + @Test(expected = IndexOutOfBoundsException.class) + public void testRemoveAllNullShortArray() { + ArrayUtils.removeAll((short[]) null, 0); + } + + @Test + public void testRemoveElementsObjectArray() { + Object[] array; + array = ArrayUtils.removeElements((Object[]) null, "a"); + assertNull(array); + array = ArrayUtils.removeElements(ArrayUtils.EMPTY_OBJECT_ARRAY, "a"); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_OBJECT_ARRAY, array)); + assertEquals(Object.class, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new Object[] { "a" }, "a"); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_OBJECT_ARRAY, array)); + assertEquals(Object.class, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new Object[] { "a", "b" }, "a"); + assertTrue(Arrays.equals(new Object[] { "b" }, array)); + assertEquals(Object.class, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new Object[] { "a", "b", "a" }, "a"); + assertTrue(Arrays.equals(new Object[] { "b", "a" }, array)); + assertEquals(Object.class, array.getClass().getComponentType()); + + array = ArrayUtils.removeElements((Object[]) null, "a", "b"); + assertNull(array); + array = ArrayUtils.removeElements(ArrayUtils.EMPTY_OBJECT_ARRAY, "a", "b"); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_OBJECT_ARRAY, array)); + assertEquals(Object.class, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new Object[] { "a" }, "a", "b"); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_OBJECT_ARRAY, array)); + assertEquals(Object.class, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new Object[] { "a", "b" }, "a", "c"); + assertTrue(Arrays.equals(new Object[] { "b" }, array)); + assertEquals(Object.class, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new Object[] { "a", "b", "a" }, "a"); + assertTrue(Arrays.equals(new Object[] { "b", "a" }, array)); + assertEquals(Object.class, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new Object[] { "a", "b", "a" }, "a", "b"); + assertTrue(Arrays.equals(new Object[] { "a" }, array)); + assertEquals(Object.class, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new Object[] { "a", "b", "a" }, "a", "a"); + assertTrue(Arrays.equals(new Object[] { "b" }, array)); + assertEquals(Object.class, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new Object[] { "a", "b", "a" }, "a", "a", "a", "a"); + assertTrue(Arrays.equals(new Object[] { "b" }, array)); + assertEquals(Object.class, array.getClass().getComponentType()); + } + + @Test + public void testRemoveElementBooleanArray() { + boolean[] array; + array = ArrayUtils.removeElements((boolean[]) null, true); + assertNull(array); + array = ArrayUtils.removeElements(ArrayUtils.EMPTY_BOOLEAN_ARRAY, true); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_BOOLEAN_ARRAY, array)); + assertEquals(Boolean.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new boolean[] { true }, true); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_BOOLEAN_ARRAY, array)); + assertEquals(Boolean.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new boolean[] { true, false }, true); + assertTrue(Arrays.equals(new boolean[] { false }, array)); + assertEquals(Boolean.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new boolean[] { true, false, true }, true); + assertTrue(Arrays.equals(new boolean[] { false, true }, array)); + assertEquals(Boolean.TYPE, array.getClass().getComponentType()); + + array = ArrayUtils.removeElements((boolean[]) null, true, false); + assertNull(array); + array = ArrayUtils.removeElements(ArrayUtils.EMPTY_BOOLEAN_ARRAY, true, false); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_BOOLEAN_ARRAY, array)); + assertEquals(Boolean.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new boolean[] { true }, true, false); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_BOOLEAN_ARRAY, array)); + assertEquals(Boolean.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new boolean[] { true, false }, true, false); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_BOOLEAN_ARRAY, array)); + assertEquals(Boolean.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new boolean[] { true, false }, true, true); + assertTrue(Arrays.equals(new boolean[] { false }, array)); + assertEquals(Boolean.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new boolean[] { true, false, true }, true, false); + assertTrue(Arrays.equals(new boolean[] { true }, array)); + assertEquals(Boolean.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new boolean[] { true, false, true }, true, true); + assertTrue(Arrays.equals(new boolean[] { false }, array)); + assertEquals(Boolean.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new boolean[] { true, false, true }, true, true, true, true); + assertTrue(Arrays.equals(new boolean[] { false }, array)); + assertEquals(Boolean.TYPE, array.getClass().getComponentType()); + } + + @Test + public void testRemoveElementByteArray() { + byte[] array; + array = ArrayUtils.removeElements((byte[]) null, (byte) 1); + assertNull(array); + array = ArrayUtils.removeElements(ArrayUtils.EMPTY_BYTE_ARRAY, (byte) 1); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_BYTE_ARRAY, array)); + assertEquals(Byte.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new byte[] { 1 }, (byte) 1); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_BYTE_ARRAY, array)); + assertEquals(Byte.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new byte[] { 1, 2 }, (byte) 1); + assertTrue(Arrays.equals(new byte[] { 2 }, array)); + assertEquals(Byte.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new byte[] { 1, 2, 1 }, (byte) 1); + assertTrue(Arrays.equals(new byte[] { 2, 1 }, array)); + assertEquals(Byte.TYPE, array.getClass().getComponentType()); + + array = ArrayUtils.removeElements((byte[]) null, (byte) 1, (byte) 2); + assertNull(array); + array = ArrayUtils.removeElements(ArrayUtils.EMPTY_BYTE_ARRAY, (byte) 1, (byte) 2); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_BYTE_ARRAY, array)); + assertEquals(Byte.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new byte[] { 1 }, (byte) 1, (byte) 2); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_BYTE_ARRAY, array)); + assertEquals(Byte.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new byte[] { 1, 2 }, (byte) 1, (byte) 2); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_BYTE_ARRAY, array)); + assertEquals(Byte.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new byte[] { 1, 2 }, (byte) 1, (byte) 1); + assertTrue(Arrays.equals(new byte[] { 2 }, array)); + assertEquals(Byte.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new byte[] { 1, 2, 1 }, (byte) 1, (byte) 2); + assertTrue(Arrays.equals(new byte[] { 1 }, array)); + assertEquals(Byte.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new byte[] { 1, 2, 1 }, (byte) 1, (byte) 1); + assertTrue(Arrays.equals(new byte[] { 2 }, array)); + assertEquals(Byte.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new byte[] { 1, 2, 1 }, (byte) 1, (byte) 1, (byte) 1, (byte) 1); + assertTrue(Arrays.equals(new byte[] { 2 }, array)); + assertEquals(Byte.TYPE, array.getClass().getComponentType()); + } + + @Test + public void testRemoveElementCharArray() { + char[] array; + array = ArrayUtils.removeElements((char[]) null, 'a'); + assertNull(array); + array = ArrayUtils.removeElements(ArrayUtils.EMPTY_CHAR_ARRAY, 'a'); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_CHAR_ARRAY, array)); + assertEquals(Character.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new char[] { 'a' }, 'a'); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_CHAR_ARRAY, array)); + assertEquals(Character.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new char[] { 'a', 'b' }, 'a'); + assertTrue(Arrays.equals(new char[] { 'b' }, array)); + assertEquals(Character.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new char[] { 'a', 'b', 'a' }, 'a'); + assertTrue(Arrays.equals(new char[] { 'b', 'a' }, array)); + assertEquals(Character.TYPE, array.getClass().getComponentType()); + + array = ArrayUtils.removeElements((char[]) null, 'a', 'b'); + assertNull(array); + array = ArrayUtils.removeElements(ArrayUtils.EMPTY_CHAR_ARRAY, 'a', 'b'); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_CHAR_ARRAY, array)); + assertEquals(Character.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new char[] { 'a' }, 'a', 'b'); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_CHAR_ARRAY, array)); + assertEquals(Character.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new char[] { 'a', 'b' }, 'a', 'b'); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_CHAR_ARRAY, array)); + assertEquals(Character.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new char[] { 'a', 'b' }, 'a', 'a'); + assertTrue(Arrays.equals(new char[] { 'b' }, array)); + assertEquals(Character.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new char[] { 'a', 'b', 'a' }, 'a', 'b'); + assertTrue(Arrays.equals(new char[] { 'a' }, array)); + assertEquals(Character.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new char[] { 'a', 'b', 'a' }, 'a', 'a'); + assertTrue(Arrays.equals(new char[] { 'b' }, array)); + assertEquals(Character.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new char[] { 'a', 'b', 'a' }, 'a', 'a', 'a', 'a'); + assertTrue(Arrays.equals(new char[] { 'b' }, array)); + assertEquals(Character.TYPE, array.getClass().getComponentType()); + } + + @Test + @SuppressWarnings("cast") + public void testRemoveElementDoubleArray() { + double[] array; + array = ArrayUtils.removeElements((double[]) null, (double) 1); + assertNull(array); + array = ArrayUtils.removeElements(ArrayUtils.EMPTY_DOUBLE_ARRAY, (double) 1); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_DOUBLE_ARRAY, array)); + assertEquals(Double.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new double[] { 1 }, (double) 1); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_DOUBLE_ARRAY, array)); + assertEquals(Double.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new double[] { 1, 2 }, (double) 1); + assertTrue(Arrays.equals(new double[] { 2 }, array)); + assertEquals(Double.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new double[] { 1, 2, 1 }, (double) 1); + assertTrue(Arrays.equals(new double[] { 2, 1 }, array)); + assertEquals(Double.TYPE, array.getClass().getComponentType()); + + array = ArrayUtils.removeElements((double[]) null, (double) 1, (double) 2); + assertNull(array); + array = ArrayUtils.removeElements(ArrayUtils.EMPTY_DOUBLE_ARRAY, (double) 1, (double) 2); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_DOUBLE_ARRAY, array)); + assertEquals(Double.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new double[] { 1 }, (double) 1, (double) 2); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_DOUBLE_ARRAY, array)); + assertEquals(Double.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new double[] { 1, 2 }, (double) 1, (double) 2); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_DOUBLE_ARRAY, array)); + assertEquals(Double.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new double[] { 1, 2 }, (double) 1, (double) 1); + assertTrue(Arrays.equals(new double[] { 2 }, array)); + assertEquals(Double.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new double[] { 1, 2, 1 }, (double) 1, (double) 2); + assertTrue(Arrays.equals(new double[] { 1 }, array)); + assertEquals(Double.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new double[] { 1, 2, 1 }, (double) 1, (double) 1); + assertTrue(Arrays.equals(new double[] { 2 }, array)); + assertEquals(Double.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new double[] { 1, 2, 1 }, (double) 1, (double) 1, (double) 1, (double) 1); + assertTrue(Arrays.equals(new double[] { 2 }, array)); + assertEquals(Double.TYPE, array.getClass().getComponentType()); + } + + @Test + @SuppressWarnings("cast") + public void testRemoveElementFloatArray() { + float[] array; + array = ArrayUtils.removeElements((float[]) null, (float) 1); + assertNull(array); + array = ArrayUtils.removeElements(ArrayUtils.EMPTY_FLOAT_ARRAY, (float) 1); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_FLOAT_ARRAY, array)); + assertEquals(Float.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new float[] { 1 }, (float) 1); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_FLOAT_ARRAY, array)); + assertEquals(Float.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new float[] { 1, 2 }, (float) 1); + assertTrue(Arrays.equals(new float[] { 2 }, array)); + assertEquals(Float.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new float[] { 1, 2, 1 }, (float) 1); + assertTrue(Arrays.equals(new float[] { 2, 1 }, array)); + assertEquals(Float.TYPE, array.getClass().getComponentType()); + + array = ArrayUtils.removeElements((float[]) null, (float) 1, (float) 1); + assertNull(array); + array = ArrayUtils.removeElements(ArrayUtils.EMPTY_FLOAT_ARRAY, (float) 1, (float) 1); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_FLOAT_ARRAY, array)); + assertEquals(Float.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new float[] { 1 }, (float) 1, (float) 1); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_FLOAT_ARRAY, array)); + assertEquals(Float.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new float[] { 1, 2 }, (float) 1, (float) 2); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_FLOAT_ARRAY, array)); + assertEquals(Float.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new float[] { 1, 2 }, (float) 1, (float) 1); + assertTrue(Arrays.equals(new float[] { 2 }, array)); + assertEquals(Float.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new float[] { 1, 2, 1 }, (float) 1, (float) 1); + assertTrue(Arrays.equals(new float[] { 2 }, array)); + assertEquals(Float.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new float[] { 1, 2, 1 }, (float) 1, (float) 2); + assertTrue(Arrays.equals(new float[] { 1 }, array)); + assertEquals(Float.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new float[] { 1, 2, 1 }, (float) 1, (float) 1, (float) 1, (float) 1); + assertTrue(Arrays.equals(new float[] { 2 }, array)); + assertEquals(Float.TYPE, array.getClass().getComponentType()); + } + + @Test + public void testRemoveElementIntArray() { + int[] array; + array = ArrayUtils.removeElements((int[]) null, 1); + assertNull(array); + array = ArrayUtils.removeElements(ArrayUtils.EMPTY_INT_ARRAY, 1); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_INT_ARRAY, array)); + assertEquals(Integer.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new int[] { 1 }, 1); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_INT_ARRAY, array)); + assertEquals(Integer.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new int[] { 1, 2 }, 1); + assertTrue(Arrays.equals(new int[] { 2 }, array)); + assertEquals(Integer.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new int[] { 1, 2, 1 }, 1); + assertTrue(Arrays.equals(new int[] { 2, 1 }, array)); + assertEquals(Integer.TYPE, array.getClass().getComponentType()); + + array = ArrayUtils.removeElements((int[]) null, 1); + assertNull(array); + array = ArrayUtils.removeElements(ArrayUtils.EMPTY_INT_ARRAY, 1, 1); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_INT_ARRAY, array)); + assertEquals(Integer.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new int[] { 1 }, 1, 1); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_INT_ARRAY, array)); + assertEquals(Integer.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new int[] { 1, 2 }, 1, 2); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_INT_ARRAY, array)); + assertEquals(Integer.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new int[] { 1, 2 }, 1, 1); + assertTrue(Arrays.equals(new int[] { 2 }, array)); + assertEquals(Integer.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new int[] { 1, 2, 1 }, 1, 2); + assertTrue(Arrays.equals(new int[] { 1 }, array)); + assertEquals(Integer.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new int[] { 1, 2, 1 }, 1, 1); + assertTrue(Arrays.equals(new int[] { 2 }, array)); + assertEquals(Integer.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new int[] { 1, 2, 1 }, 1, 1, 1, 1); + assertTrue(Arrays.equals(new int[] { 2 }, array)); + assertEquals(Integer.TYPE, array.getClass().getComponentType()); + } + + @Test + @SuppressWarnings("cast") + public void testRemoveElementLongArray() { + long[] array; + array = ArrayUtils.removeElements((long[]) null, (long) 1); + assertNull(array); + array = ArrayUtils.removeElements(ArrayUtils.EMPTY_LONG_ARRAY, (long) 1); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_LONG_ARRAY, array)); + assertEquals(Long.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new long[] { 1 }, (long) 1); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_LONG_ARRAY, array)); + assertEquals(Long.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new long[] { 1, 2 }, (long) 1); + assertTrue(Arrays.equals(new long[] { 2 }, array)); + assertEquals(Long.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new long[] { 1, 2, 1 }, (long) 1); + assertTrue(Arrays.equals(new long[] { 2, 1 }, array)); + assertEquals(Long.TYPE, array.getClass().getComponentType()); + + array = ArrayUtils.removeElements((long[]) null, (long) 1, (long) 1); + assertNull(array); + array = ArrayUtils.removeElements(ArrayUtils.EMPTY_LONG_ARRAY, (long) 1, (long) 1); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_LONG_ARRAY, array)); + assertEquals(Long.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new long[] { 1 }, (long) 1, (long) 1); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_LONG_ARRAY, array)); + assertEquals(Long.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new long[] { 1, 2 }, (long) 1, (long) 2); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_LONG_ARRAY, array)); + assertEquals(Long.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new long[] { 1, 2 }, (long) 1, (long) 1); + assertTrue(Arrays.equals(new long[] { 2 }, array)); + assertEquals(Long.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new long[] { 1, 2, 1 }, (long) 1, (long) 1); + assertTrue(Arrays.equals(new long[] { 2 }, array)); + assertEquals(Long.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new long[] { 1, 2, 1 }, (long) 1, (long) 2); + assertTrue(Arrays.equals(new long[] { 1 }, array)); + assertEquals(Long.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new long[] { 1, 2, 1 }, (long) 1, (long) 1, (long) 1, (long) 1); + assertTrue(Arrays.equals(new long[] { 2 }, array)); + assertEquals(Long.TYPE, array.getClass().getComponentType()); + } + + @Test + public void testRemoveElementShortArray() { + short[] array; + array = ArrayUtils.removeElements((short[]) null, (short) 1); + assertNull(array); + array = ArrayUtils.removeElements(ArrayUtils.EMPTY_SHORT_ARRAY, (short) 1); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_SHORT_ARRAY, array)); + assertEquals(Short.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new short[] { 1 }, (short) 1); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_SHORT_ARRAY, array)); + assertEquals(Short.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new short[] { 1, 2 }, (short) 1); + assertTrue(Arrays.equals(new short[] { 2 }, array)); + assertEquals(Short.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new short[] { 1, 2, 1 }, (short) 1); + assertTrue(Arrays.equals(new short[] { 2, 1 }, array)); + assertEquals(Short.TYPE, array.getClass().getComponentType()); + + array = ArrayUtils.removeElements((short[]) null, (short) 1, (short) 1); + assertNull(array); + array = ArrayUtils.removeElements(ArrayUtils.EMPTY_SHORT_ARRAY, (short) 1, (short) 1); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_SHORT_ARRAY, array)); + assertEquals(Short.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new short[] { 1 }, (short) 1, (short) 1); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_SHORT_ARRAY, array)); + assertEquals(Short.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new short[] { 1, 2 }, (short) 1, (short) 2); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_SHORT_ARRAY, array)); + assertEquals(Short.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new short[] { 1, 2 }, (short) 1, (short) 1); + assertTrue(Arrays.equals(new short[] { 2 }, array)); + assertEquals(Short.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new short[] { 1, 2, 1 }, (short) 1, (short) 1); + assertTrue(Arrays.equals(new short[] { 2 }, array)); + assertEquals(Short.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new short[] { 1, 2, 1 }, (short) 1, (short) 2); + assertTrue(Arrays.equals(new short[] { 1 }, array)); + assertEquals(Short.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElements(new short[] { 1, 2, 1 }, (short) 1, (short) 1, (short) 1, (short) 1); + assertTrue(Arrays.equals(new short[] { 2 }, array)); + assertEquals(Short.TYPE, array.getClass().getComponentType()); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/ArrayUtilsRemoveTest.java b/ApacheCommonsLang/org/apache/commons/lang3/ArrayUtilsRemoveTest.java new file mode 100644 index 0000000..5300878 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/ArrayUtilsRemoveTest.java @@ -0,0 +1,487 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.lang3; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.util.Arrays; + +import org.junit.Test; + +/** + * Tests ArrayUtils remove and removeElement methods. + * + * @version $Id: ArrayUtilsRemoveTest.java 1158284 2011-08-16 14:11:31Z ggregory $ + */ +public class ArrayUtilsRemoveTest { + + @Test + public void testRemoveObjectArray() { + Object[] array; + array = ArrayUtils.remove(new Object[] {"a"}, 0); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_OBJECT_ARRAY, array)); + assertEquals(Object.class, array.getClass().getComponentType()); + array = ArrayUtils.remove(new Object[] {"a", "b"}, 0); + assertTrue(Arrays.equals(new Object[] {"b"}, array)); + assertEquals(Object.class, array.getClass().getComponentType()); + array = ArrayUtils.remove(new Object[] {"a", "b"}, 1); + assertTrue(Arrays.equals(new Object[] {"a"}, array)); + assertEquals(Object.class, array.getClass().getComponentType()); + array = ArrayUtils.remove(new Object[] {"a", "b", "c"}, 1); + assertTrue(Arrays.equals(new Object[] {"a", "c"}, array)); + assertEquals(Object.class, array.getClass().getComponentType()); + try { + ArrayUtils.remove(new Object[] {"a", "b"}, -1); + fail("IndexOutOfBoundsException expected"); + } catch (IndexOutOfBoundsException e) {} + try { + ArrayUtils.remove(new Object[] {"a", "b"}, 2); + fail("IndexOutOfBoundsException expected"); + } catch (IndexOutOfBoundsException e) {} + try { + ArrayUtils.remove((Object[]) null, 0); + fail("IndexOutOfBoundsException expected"); + } catch (IndexOutOfBoundsException e) {} + } + + @Test + public void testRemoveNumberArray(){ + Number[] inarray = {Integer.valueOf(1),Long.valueOf(2),Byte.valueOf((byte) 3)}; + assertEquals(3, inarray.length); + Number[] outarray; + outarray = ArrayUtils.remove(inarray, 1); + assertEquals(2, outarray.length); + assertEquals(Number.class, outarray.getClass().getComponentType()); + outarray = ArrayUtils.remove(outarray, 1); + assertEquals(1, outarray.length); + assertEquals(Number.class, outarray.getClass().getComponentType()); + outarray = ArrayUtils.remove(outarray, 0); + assertEquals(0, outarray.length); + assertEquals(Number.class, outarray.getClass().getComponentType()); + } + + @Test + public void testRemoveBooleanArray() { + boolean[] array; + array = ArrayUtils.remove(new boolean[] {true}, 0); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_BOOLEAN_ARRAY, array)); + assertEquals(Boolean.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.remove(new boolean[] {true, false}, 0); + assertTrue(Arrays.equals(new boolean[] {false}, array)); + assertEquals(Boolean.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.remove(new boolean[] {true, false}, 1); + assertTrue(Arrays.equals(new boolean[] {true}, array)); + assertEquals(Boolean.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.remove(new boolean[] {true, false, true}, 1); + assertTrue(Arrays.equals(new boolean[] {true, true}, array)); + assertEquals(Boolean.TYPE, array.getClass().getComponentType()); + try { + ArrayUtils.remove(new boolean[] {true, false}, -1); + fail("IndexOutOfBoundsException expected"); + } catch (IndexOutOfBoundsException e) {} + try { + ArrayUtils.remove(new boolean[] {true, false}, 2); + fail("IndexOutOfBoundsException expected"); + } catch (IndexOutOfBoundsException e) {} + try { + ArrayUtils.remove((boolean[]) null, 0); + fail("IndexOutOfBoundsException expected"); + } catch (IndexOutOfBoundsException e) {} + } + + @Test + public void testRemoveByteArray() { + byte[] array; + array = ArrayUtils.remove(new byte[] {1}, 0); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_BYTE_ARRAY, array)); + assertEquals(Byte.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.remove(new byte[] {1, 2}, 0); + assertTrue(Arrays.equals(new byte[] {2}, array)); + assertEquals(Byte.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.remove(new byte[] {1, 2}, 1); + assertTrue(Arrays.equals(new byte[] {1}, array)); + assertEquals(Byte.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.remove(new byte[] {1, 2, 1}, 1); + assertTrue(Arrays.equals(new byte[] {1, 1}, array)); + assertEquals(Byte.TYPE, array.getClass().getComponentType()); + try { + ArrayUtils.remove(new byte[] {1, 2}, -1); + fail("IndexOutOfBoundsException expected"); + } catch (IndexOutOfBoundsException e) {} + try { + ArrayUtils.remove(new byte[] {1, 2}, 2); + fail("IndexOutOfBoundsException expected"); + } catch (IndexOutOfBoundsException e) {} + try { + ArrayUtils.remove((byte[]) null, 0); + fail("IndexOutOfBoundsException expected"); + } catch (IndexOutOfBoundsException e) {} + } + + @Test + public void testRemoveCharArray() { + char[] array; + array = ArrayUtils.remove(new char[] {'a'}, 0); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_CHAR_ARRAY, array)); + assertEquals(Character.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.remove(new char[] {'a', 'b'}, 0); + assertTrue(Arrays.equals(new char[] {'b'}, array)); + assertEquals(Character.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.remove(new char[] {'a', 'b'}, 1); + assertTrue(Arrays.equals(new char[] {'a'}, array)); + assertEquals(Character.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.remove(new char[] {'a', 'b', 'c'}, 1); + assertTrue(Arrays.equals(new char[] {'a', 'c'}, array)); + assertEquals(Character.TYPE, array.getClass().getComponentType()); + try { + ArrayUtils.remove(new char[] {'a', 'b'}, -1); + fail("IndexOutOfBoundsException expected"); + } catch (IndexOutOfBoundsException e) {} + try { + ArrayUtils.remove(new char[] {'a', 'b'}, 2); + fail("IndexOutOfBoundsException expected"); + } catch (IndexOutOfBoundsException e) {} + try { + ArrayUtils.remove((char[]) null, 0); + fail("IndexOutOfBoundsException expected"); + } catch (IndexOutOfBoundsException e) {} + } + + @Test + public void testRemoveDoubleArray() { + double[] array; + array = ArrayUtils.remove(new double[] {1}, 0); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_DOUBLE_ARRAY, array)); + assertEquals(Double.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.remove(new double[] {1, 2}, 0); + assertTrue(Arrays.equals(new double[] {2}, array)); + assertEquals(Double.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.remove(new double[] {1, 2}, 1); + assertTrue(Arrays.equals(new double[] {1}, array)); + assertEquals(Double.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.remove(new double[] {1, 2, 1}, 1); + assertTrue(Arrays.equals(new double[] {1, 1}, array)); + assertEquals(Double.TYPE, array.getClass().getComponentType()); + try { + ArrayUtils.remove(new double[] {1, 2}, -1); + fail("IndexOutOfBoundsException expected"); + } catch (IndexOutOfBoundsException e) {} + try { + ArrayUtils.remove(new double[] {1, 2}, 2); + fail("IndexOutOfBoundsException expected"); + } catch (IndexOutOfBoundsException e) {} + try { + ArrayUtils.remove((double[]) null, 0); + fail("IndexOutOfBoundsException expected"); + } catch (IndexOutOfBoundsException e) {} + } + + @Test + public void testRemoveFloatArray() { + float[] array; + array = ArrayUtils.remove(new float[] {1}, 0); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_FLOAT_ARRAY, array)); + assertEquals(Float.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.remove(new float[] {1, 2}, 0); + assertTrue(Arrays.equals(new float[] {2}, array)); + assertEquals(Float.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.remove(new float[] {1, 2}, 1); + assertTrue(Arrays.equals(new float[] {1}, array)); + assertEquals(Float.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.remove(new float[] {1, 2, 1}, 1); + assertTrue(Arrays.equals(new float[] {1, 1}, array)); + assertEquals(Float.TYPE, array.getClass().getComponentType()); + try { + ArrayUtils.remove(new float[] {1, 2}, -1); + fail("IndexOutOfBoundsException expected"); + } catch (IndexOutOfBoundsException e) {} + try { + ArrayUtils.remove(new float[] {1, 2}, 2); + fail("IndexOutOfBoundsException expected"); + } catch (IndexOutOfBoundsException e) {} + try { + ArrayUtils.remove((float[]) null, 0); + fail("IndexOutOfBoundsException expected"); + } catch (IndexOutOfBoundsException e) {} + } + + @Test + public void testRemoveIntArray() { + int[] array; + array = ArrayUtils.remove(new int[] {1}, 0); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_INT_ARRAY, array)); + assertEquals(Integer.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.remove(new int[] {1, 2}, 0); + assertTrue(Arrays.equals(new int[] {2}, array)); + assertEquals(Integer.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.remove(new int[] {1, 2}, 1); + assertTrue(Arrays.equals(new int[] {1}, array)); + assertEquals(Integer.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.remove(new int[] {1, 2, 1}, 1); + assertTrue(Arrays.equals(new int[] {1, 1}, array)); + assertEquals(Integer.TYPE, array.getClass().getComponentType()); + try { + ArrayUtils.remove(new int[] {1, 2}, -1); + fail("IndexOutOfBoundsException expected"); + } catch (IndexOutOfBoundsException e) {} + try { + ArrayUtils.remove(new int[] {1, 2}, 2); + fail("IndexOutOfBoundsException expected"); + } catch (IndexOutOfBoundsException e) {} + try { + ArrayUtils.remove((int[]) null, 0); + fail("IndexOutOfBoundsException expected"); + } catch (IndexOutOfBoundsException e) {} + } + + @Test + public void testRemoveLongArray() { + long[] array; + array = ArrayUtils.remove(new long[] {1}, 0); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_LONG_ARRAY, array)); + assertEquals(Long.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.remove(new long[] {1, 2}, 0); + assertTrue(Arrays.equals(new long[] {2}, array)); + assertEquals(Long.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.remove(new long[] {1, 2}, 1); + assertTrue(Arrays.equals(new long[] {1}, array)); + assertEquals(Long.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.remove(new long[] {1, 2, 1}, 1); + assertTrue(Arrays.equals(new long[] {1, 1}, array)); + assertEquals(Long.TYPE, array.getClass().getComponentType()); + try { + ArrayUtils.remove(new long[] {1, 2}, -1); + fail("IndexOutOfBoundsException expected"); + } catch (IndexOutOfBoundsException e) {} + try { + ArrayUtils.remove(new long[] {1, 2}, 2); + fail("IndexOutOfBoundsException expected"); + } catch (IndexOutOfBoundsException e) {} + try { + ArrayUtils.remove((long[]) null, 0); + fail("IndexOutOfBoundsException expected"); + } catch (IndexOutOfBoundsException e) {} + } + + @Test + public void testRemoveShortArray() { + short[] array; + array = ArrayUtils.remove(new short[] {1}, 0); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_SHORT_ARRAY, array)); + assertEquals(Short.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.remove(new short[] {1, 2}, 0); + assertTrue(Arrays.equals(new short[] {2}, array)); + assertEquals(Short.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.remove(new short[] {1, 2}, 1); + assertTrue(Arrays.equals(new short[] {1}, array)); + assertEquals(Short.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.remove(new short[] {1, 2, 1}, 1); + assertTrue(Arrays.equals(new short[] {1, 1}, array)); + assertEquals(Short.TYPE, array.getClass().getComponentType()); + try { + ArrayUtils.remove(new short[] {1, 2}, -1); + fail("IndexOutOfBoundsException expected"); + } catch (IndexOutOfBoundsException e) {} + try { + ArrayUtils.remove(new short[] {1, 2}, 2); + fail("IndexOutOfBoundsException expected"); + } catch (IndexOutOfBoundsException e) {} + try { + ArrayUtils.remove((short[]) null, 0); + fail("IndexOutOfBoundsException expected"); + } catch (IndexOutOfBoundsException e) {} + } + + @Test + public void testRemoveElementObjectArray() { + Object[] array; + array = ArrayUtils.removeElement((Object[]) null, "a"); + assertNull(array); + array = ArrayUtils.removeElement(ArrayUtils.EMPTY_OBJECT_ARRAY, "a"); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_OBJECT_ARRAY, array)); + assertEquals(Object.class, array.getClass().getComponentType()); + array = ArrayUtils.removeElement(new Object[] {"a"}, "a"); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_OBJECT_ARRAY, array)); + assertEquals(Object.class, array.getClass().getComponentType()); + array = ArrayUtils.removeElement(new Object[] {"a", "b"}, "a"); + assertTrue(Arrays.equals(new Object[] {"b"}, array)); + assertEquals(Object.class, array.getClass().getComponentType()); + array = ArrayUtils.removeElement(new Object[] {"a", "b", "a"}, "a"); + assertTrue(Arrays.equals(new Object[] {"b", "a"}, array)); + assertEquals(Object.class, array.getClass().getComponentType()); + } + + @Test + public void testRemoveElementBooleanArray() { + boolean[] array; + array = ArrayUtils.removeElement((boolean[]) null, true); + assertNull(array); + array = ArrayUtils.removeElement(ArrayUtils.EMPTY_BOOLEAN_ARRAY, true); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_BOOLEAN_ARRAY, array)); + assertEquals(Boolean.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElement(new boolean[] {true}, true); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_BOOLEAN_ARRAY, array)); + assertEquals(Boolean.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElement(new boolean[] {true, false}, true); + assertTrue(Arrays.equals(new boolean[] {false}, array)); + assertEquals(Boolean.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElement(new boolean[] {true, false, true}, true); + assertTrue(Arrays.equals(new boolean[] {false, true}, array)); + assertEquals(Boolean.TYPE, array.getClass().getComponentType()); + } + + @Test + public void testRemoveElementByteArray() { + byte[] array; + array = ArrayUtils.removeElement((byte[]) null, (byte) 1); + assertNull(array); + array = ArrayUtils.removeElement(ArrayUtils.EMPTY_BYTE_ARRAY, (byte) 1); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_BYTE_ARRAY, array)); + assertEquals(Byte.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElement(new byte[] {1}, (byte) 1); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_BYTE_ARRAY, array)); + assertEquals(Byte.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElement(new byte[] {1, 2}, (byte) 1); + assertTrue(Arrays.equals(new byte[] {2}, array)); + assertEquals(Byte.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElement(new byte[] {1, 2, 1}, (byte) 1); + assertTrue(Arrays.equals(new byte[] {2, 1}, array)); + assertEquals(Byte.TYPE, array.getClass().getComponentType()); + } + + @Test + public void testRemoveElementCharArray() { + char[] array; + array = ArrayUtils.removeElement((char[]) null, 'a'); + assertNull(array); + array = ArrayUtils.removeElement(ArrayUtils.EMPTY_CHAR_ARRAY, 'a'); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_CHAR_ARRAY, array)); + assertEquals(Character.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElement(new char[] {'a'}, 'a'); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_CHAR_ARRAY, array)); + assertEquals(Character.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElement(new char[] {'a', 'b'}, 'a'); + assertTrue(Arrays.equals(new char[] {'b'}, array)); + assertEquals(Character.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElement(new char[] {'a', 'b', 'a'}, 'a'); + assertTrue(Arrays.equals(new char[] {'b', 'a'}, array)); + assertEquals(Character.TYPE, array.getClass().getComponentType()); + } + + @Test + @SuppressWarnings("cast") + public void testRemoveElementDoubleArray() { + double[] array; + array = ArrayUtils.removeElement((double[]) null, (double) 1); + assertNull(array); + array = ArrayUtils.removeElement(ArrayUtils.EMPTY_DOUBLE_ARRAY, (double) 1); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_DOUBLE_ARRAY, array)); + assertEquals(Double.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElement(new double[] {1}, (double) 1); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_DOUBLE_ARRAY, array)); + assertEquals(Double.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElement(new double[] {1, 2}, (double) 1); + assertTrue(Arrays.equals(new double[] {2}, array)); + assertEquals(Double.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElement(new double[] {1, 2, 1}, (double) 1); + assertTrue(Arrays.equals(new double[] {2, 1}, array)); + assertEquals(Double.TYPE, array.getClass().getComponentType()); + } + + @Test + @SuppressWarnings("cast") + public void testRemoveElementFloatArray() { + float[] array; + array = ArrayUtils.removeElement((float[]) null, (float) 1); + assertNull(array); + array = ArrayUtils.removeElement(ArrayUtils.EMPTY_FLOAT_ARRAY, (float) 1); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_FLOAT_ARRAY, array)); + assertEquals(Float.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElement(new float[] {1}, (float) 1); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_FLOAT_ARRAY, array)); + assertEquals(Float.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElement(new float[] {1, 2}, (float) 1); + assertTrue(Arrays.equals(new float[] {2}, array)); + assertEquals(Float.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElement(new float[] {1, 2, 1}, (float) 1); + assertTrue(Arrays.equals(new float[] {2, 1}, array)); + assertEquals(Float.TYPE, array.getClass().getComponentType()); + } + + @Test + public void testRemoveElementIntArray() { + int[] array; + array = ArrayUtils.removeElement((int[]) null, 1); + assertNull(array); + array = ArrayUtils.removeElement(ArrayUtils.EMPTY_INT_ARRAY, 1); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_INT_ARRAY, array)); + assertEquals(Integer.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElement(new int[] {1}, 1); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_INT_ARRAY, array)); + assertEquals(Integer.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElement(new int[] {1, 2}, 1); + assertTrue(Arrays.equals(new int[] {2}, array)); + assertEquals(Integer.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElement(new int[] {1, 2, 1}, 1); + assertTrue(Arrays.equals(new int[] {2, 1}, array)); + assertEquals(Integer.TYPE, array.getClass().getComponentType()); + } + + @Test + @SuppressWarnings("cast") + public void testRemoveElementLongArray() { + long[] array; + array = ArrayUtils.removeElement((long[]) null, (long) 1); + assertNull(array); + array = ArrayUtils.removeElement(ArrayUtils.EMPTY_LONG_ARRAY, (long) 1); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_LONG_ARRAY, array)); + assertEquals(Long.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElement(new long[] {1}, (long) 1); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_LONG_ARRAY, array)); + assertEquals(Long.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElement(new long[] {1, 2}, (long) 1); + assertTrue(Arrays.equals(new long[] {2}, array)); + assertEquals(Long.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElement(new long[] {1, 2, 1}, (long) 1); + assertTrue(Arrays.equals(new long[] {2, 1}, array)); + assertEquals(Long.TYPE, array.getClass().getComponentType()); + } + + @Test + public void testRemoveElementShortArray() { + short[] array; + array = ArrayUtils.removeElement((short[]) null, (short) 1); + assertNull(array); + array = ArrayUtils.removeElement(ArrayUtils.EMPTY_SHORT_ARRAY, (short) 1); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_SHORT_ARRAY, array)); + assertEquals(Short.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElement(new short[] {1}, (short) 1); + assertTrue(Arrays.equals(ArrayUtils.EMPTY_SHORT_ARRAY, array)); + assertEquals(Short.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElement(new short[] {1, 2}, (short) 1); + assertTrue(Arrays.equals(new short[] {2}, array)); + assertEquals(Short.TYPE, array.getClass().getComponentType()); + array = ArrayUtils.removeElement(new short[] {1, 2, 1}, (short) 1); + assertTrue(Arrays.equals(new short[] {2, 1}, array)); + assertEquals(Short.TYPE, array.getClass().getComponentType()); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/ArrayUtilsTest.java b/ApacheCommonsLang/org/apache/commons/lang3/ArrayUtilsTest.java new file mode 100644 index 0000000..3eb1691 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/ArrayUtilsTest.java @@ -0,0 +1,2922 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.Date; +import java.util.Map; + +import junit.framework.TestCase; + +/** + * Unit tests {@link org.apache.commons.lang3.ArrayUtils}. + * + * @version $Id: ArrayUtilsTest.java 1309977 2012-04-05 17:53:39Z ggregory $ + */ +public class ArrayUtilsTest extends TestCase { + + public ArrayUtilsTest(String name) { + super(name); + } + + //----------------------------------------------------------------------- + public void testConstructor() { + assertNotNull(new ArrayUtils()); + Constructor[] cons = ArrayUtils.class.getDeclaredConstructors(); + assertEquals(1, cons.length); + assertEquals(true, Modifier.isPublic(cons[0].getModifiers())); + assertEquals(true, Modifier.isPublic(ArrayUtils.class.getModifiers())); + assertEquals(false, Modifier.isFinal(ArrayUtils.class.getModifiers())); + } + + //----------------------------------------------------------------------- + public void testToString() { + assertEquals("{}", ArrayUtils.toString(null)); + assertEquals("{}", ArrayUtils.toString(new Object[0])); + assertEquals("{}", ArrayUtils.toString(new String[0])); + assertEquals("{}", ArrayUtils.toString(new String[] {null})); + assertEquals("{pink,blue}", ArrayUtils.toString(new String[] {"pink","blue"})); + + assertEquals("", ArrayUtils.toString(null, "")); + assertEquals("{}", ArrayUtils.toString(new Object[0], "")); + assertEquals("{}", ArrayUtils.toString(new String[0], "")); + assertEquals("{}", ArrayUtils.toString(new String[] {null}, "")); + assertEquals("{pink,blue}", ArrayUtils.toString(new String[] {"pink","blue"}, "")); + } + + //----------------------------------------------------------------------- + public void testHashCode() { + long[][] array1 = new long[][] {{2,5}, {4,5}}; + long[][] array2 = new long[][] {{2,5}, {4,6}}; + assertEquals(true, ArrayUtils.hashCode(array1) == ArrayUtils.hashCode(array1)); + assertEquals(false, ArrayUtils.hashCode(array1) == ArrayUtils.hashCode(array2)); + + Object[] array3 = new Object[] {new String(new char[] {'A', 'B'})}; + Object[] array4 = new Object[] {"AB"}; + assertEquals(true, ArrayUtils.hashCode(array3) == ArrayUtils.hashCode(array3)); + assertEquals(true, ArrayUtils.hashCode(array3) == ArrayUtils.hashCode(array4)); + + Object[] arrayA = new Object[] {new boolean[] {true, false}, new int[] {6, 7}}; + Object[] arrayB = new Object[] {new boolean[] {true, false}, new int[] {6, 7}}; + assertEquals(true, ArrayUtils.hashCode(arrayB) == ArrayUtils.hashCode(arrayA)); + } + + //----------------------------------------------------------------------- + private void assertIsEquals(Object array1, Object array2, Object array3) { + assertEquals(true, ArrayUtils.isEquals(array1, array1)); + assertEquals(true, ArrayUtils.isEquals(array2, array2)); + assertEquals(true, ArrayUtils.isEquals(array3, array3)); + assertEquals(false, ArrayUtils.isEquals(array1, array2)); + assertEquals(false, ArrayUtils.isEquals(array2, array1)); + assertEquals(false, ArrayUtils.isEquals(array1, array3)); + assertEquals(false, ArrayUtils.isEquals(array3, array1)); + assertEquals(false, ArrayUtils.isEquals(array1, array2)); + assertEquals(false, ArrayUtils.isEquals(array2, array1)); + } + + public void testIsEquals() { + long[][] larray1 = new long[][]{{2, 5}, {4, 5}}; + long[][] larray2 = new long[][]{{2, 5}, {4, 6}}; + long[] larray3 = new long[]{2, 5}; + this.assertIsEquals(larray1, larray2, larray3); + + int[][] iarray1 = new int[][]{{2, 5}, {4, 5}}; + int[][] iarray2 = new int[][]{{2, 5}, {4, 6}}; + int[] iarray3 = new int[]{2, 5}; + this.assertIsEquals(iarray1, iarray2, iarray3); + + short[][] sarray1 = new short[][]{{2, 5}, {4, 5}}; + short[][] sarray2 = new short[][]{{2, 5}, {4, 6}}; + short[] sarray3 = new short[]{2, 5}; + this.assertIsEquals(sarray1, sarray2, sarray3); + + float[][] farray1 = new float[][]{{2, 5}, {4, 5}}; + float[][] farray2 = new float[][]{{2, 5}, {4, 6}}; + float[] farray3 = new float[]{2, 5}; + this.assertIsEquals(farray1, farray2, farray3); + + double[][] darray1 = new double[][]{{2, 5}, {4, 5}}; + double[][] darray2 = new double[][]{{2, 5}, {4, 6}}; + double[] darray3 = new double[]{2, 5}; + this.assertIsEquals(darray1, darray2, darray3); + + byte[][] byteArray1 = new byte[][]{{2, 5}, {4, 5}}; + byte[][] byteArray2 = new byte[][]{{2, 5}, {4, 6}}; + byte[] byteArray3 = new byte[]{2, 5}; + this.assertIsEquals(byteArray1, byteArray2, byteArray3); + + char[][] charArray1 = new char[][]{{2, 5}, {4, 5}}; + char[][] charArray2 = new char[][]{{2, 5}, {4, 6}}; + char[] charArray3 = new char[]{2, 5}; + this.assertIsEquals(charArray1, charArray2, charArray3); + + boolean[][] barray1 = new boolean[][]{{true, false}, {true, true}}; + boolean[][] barray2 = new boolean[][]{{true, false}, {true, false}}; + boolean[] barray3 = new boolean[]{false, true}; + this.assertIsEquals(barray1, barray2, barray3); + + Object[] array3 = new Object[]{new String(new char[]{'A', 'B'})}; + Object[] array4 = new Object[]{"AB"}; + assertEquals(true, ArrayUtils.isEquals(array3, array3)); + assertEquals(true, ArrayUtils.isEquals(array3, array4)); + + assertEquals(true, ArrayUtils.isEquals(null, null)); + assertEquals(false, ArrayUtils.isEquals(null, array4)); + } + + //----------------------------------------------------------------------- + /** + * Tests generic array creation with parameters of same type. + */ + public void testArrayCreation() + { + final String[] array = ArrayUtils.toArray("foo", "bar"); + assertEquals(2, array.length); + assertEquals("foo", array[0]); + assertEquals("bar", array[1]); + } + + /** + * Tests generic array creation with general return type. + */ + public void testArrayCreationWithGeneralReturnType() + { + final Object obj = ArrayUtils.toArray("foo", "bar"); + assertTrue(obj instanceof String[]); + } + + /** + * Tests generic array creation with parameters of common base type. + */ + public void testArrayCreationWithDifferentTypes() + { + final Number[] array = ArrayUtils.toArray(Integer.valueOf(42), Double.valueOf(Math.PI)); + assertEquals(2, array.length); + assertEquals(Integer.valueOf(42), array[0]); + assertEquals(Double.valueOf(Math.PI), array[1]); + } + + /** + * Tests generic array creation with generic type. + */ + public void testIndirectArrayCreation() + { + final String[] array = toArrayPropagatingType("foo", "bar"); + assertEquals(2, array.length); + assertEquals("foo", array[0]); + assertEquals("bar", array[1]); + } + + /** + * Tests generic empty array creation with generic type. + */ + public void testEmptyArrayCreation() + { + final String[] array = ArrayUtils.toArray(); + assertEquals(0, array.length); + } + + /** + * Tests indirect generic empty array creation with generic type. + */ + public void testIndirectEmptyArrayCreation() + { + final String[] array = ArrayUtilsTest.toArrayPropagatingType(); + assertEquals(0, array.length); + } + + private static T[] toArrayPropagatingType(final T... items) + { + return ArrayUtils.toArray(items); + } + + //----------------------------------------------------------------------- + public void testToMap() { + Map map = ArrayUtils.toMap(new String[][] {{"foo", "bar"}, {"hello", "world"}}); + + assertEquals("bar", map.get("foo")); + assertEquals("world", map.get("hello")); + + assertEquals(null, ArrayUtils.toMap(null)); + try { + ArrayUtils.toMap(new String[][] {{"foo", "bar"}, {"short"}}); + fail("exception expected"); + } catch (IllegalArgumentException ex) {} + try { + ArrayUtils.toMap(new Object[] {new Object[] {"foo", "bar"}, "illegal type"}); + fail("exception expected"); + } catch (IllegalArgumentException ex) {} + try { + ArrayUtils.toMap(new Object[] {new Object[] {"foo", "bar"}, null}); + fail("exception expected"); + } catch (IllegalArgumentException ex) {} + + map = ArrayUtils.toMap(new Object[] {new Map.Entry() { + @Override + public Object getKey() { + return "foo"; + } + @Override + public Object getValue() { + return "bar"; + } + @Override + public Object setValue(Object value) { + throw new UnsupportedOperationException(); + } + @Override + public boolean equals(Object o) { + throw new UnsupportedOperationException(); + } + @Override + public int hashCode() { + throw new UnsupportedOperationException(); + } + }}); + assertEquals("bar", map.get("foo")); + } + + //----------------------------------------------------------------------- + public void testClone() { + assertEquals(null, ArrayUtils.clone((Object[]) null)); + Object[] original1 = new Object[0]; + Object[] cloned1 = ArrayUtils.clone(original1); + assertTrue(Arrays.equals(original1, cloned1)); + assertTrue(original1 != cloned1); + + StringBuffer buf = new StringBuffer("pick"); + original1 = new Object[] {buf, "a", new String[] {"stick"}}; + cloned1 = ArrayUtils.clone(original1); + assertTrue(Arrays.equals(original1, cloned1)); + assertTrue(original1 != cloned1); + assertSame(original1[0], cloned1[0]); + assertSame(original1[1], cloned1[1]); + assertSame(original1[2], cloned1[2]); + } + + public void testCloneBoolean() { + assertEquals(null, ArrayUtils.clone((boolean[]) null)); + boolean[] original = new boolean[] {true, false}; + boolean[] cloned = ArrayUtils.clone(original); + assertTrue(Arrays.equals(original, cloned)); + assertTrue(original != cloned); + } + + public void testCloneLong() { + assertEquals(null, ArrayUtils.clone((long[]) null)); + long[] original = new long[] {0L, 1L}; + long[] cloned = ArrayUtils.clone(original); + assertTrue(Arrays.equals(original, cloned)); + assertTrue(original != cloned); + } + + public void testCloneInt() { + assertEquals(null, ArrayUtils.clone((int[]) null)); + int[] original = new int[] {5, 8}; + int[] cloned = ArrayUtils.clone(original); + assertTrue(Arrays.equals(original, cloned)); + assertTrue(original != cloned); + } + + public void testCloneShort() { + assertEquals(null, ArrayUtils.clone((short[]) null)); + short[] original = new short[] {1, 4}; + short[] cloned = ArrayUtils.clone(original); + assertTrue(Arrays.equals(original, cloned)); + assertTrue(original != cloned); + } + + public void testCloneChar() { + assertEquals(null, ArrayUtils.clone((char[]) null)); + char[] original = new char[] {'a', '4'}; + char[] cloned = ArrayUtils.clone(original); + assertTrue(Arrays.equals(original, cloned)); + assertTrue(original != cloned); + } + + public void testCloneByte() { + assertEquals(null, ArrayUtils.clone((byte[]) null)); + byte[] original = new byte[] {1, 6}; + byte[] cloned = ArrayUtils.clone(original); + assertTrue(Arrays.equals(original, cloned)); + assertTrue(original != cloned); + } + + public void testCloneDouble() { + assertEquals(null, ArrayUtils.clone((double[]) null)); + double[] original = new double[] {2.4d, 5.7d}; + double[] cloned = ArrayUtils.clone(original); + assertTrue(Arrays.equals(original, cloned)); + assertTrue(original != cloned); + } + + public void testCloneFloat() { + assertEquals(null, ArrayUtils.clone((float[]) null)); + float[] original = new float[] {2.6f, 6.4f}; + float[] cloned = ArrayUtils.clone(original); + assertTrue(Arrays.equals(original, cloned)); + assertTrue(original != cloned); + } + + //----------------------------------------------------------------------- + + public void testNullToEmptyBoolean() { + // Test null handling + assertEquals(ArrayUtils.EMPTY_BOOLEAN_ARRAY, ArrayUtils.nullToEmpty((boolean[]) null)); + // Test valid array handling + boolean[] original = new boolean[] {true, false}; + assertEquals(original, ArrayUtils.nullToEmpty(original)); + // Test empty array handling + boolean[] empty = new boolean[]{}; + boolean[] result = ArrayUtils.nullToEmpty(empty); + assertEquals(ArrayUtils.EMPTY_BOOLEAN_ARRAY, result); + assertTrue(empty != result); + } + + public void testNullToEmptyLong() { + // Test null handling + assertEquals(ArrayUtils.EMPTY_LONG_ARRAY, ArrayUtils.nullToEmpty((long[]) null)); + // Test valid array handling + long[] original = new long[] {1L, 2L}; + assertEquals(original, ArrayUtils.nullToEmpty(original)); + // Test empty array handling + long[] empty = new long[]{}; + long[] result = ArrayUtils.nullToEmpty(empty); + assertEquals(ArrayUtils.EMPTY_LONG_ARRAY, result); + assertTrue(empty != result); + } + + public void testNullToEmptyInt() { + // Test null handling + assertEquals(ArrayUtils.EMPTY_INT_ARRAY, ArrayUtils.nullToEmpty((int[]) null)); + // Test valid array handling + int[] original = new int[] {1, 2}; + assertEquals(original, ArrayUtils.nullToEmpty(original)); + // Test empty array handling + int[] empty = new int[]{}; + int[] result = ArrayUtils.nullToEmpty(empty); + assertEquals(ArrayUtils.EMPTY_INT_ARRAY, result); + assertTrue(empty != result); + } + + public void testNullToEmptyShort() { + // Test null handling + assertEquals(ArrayUtils.EMPTY_SHORT_ARRAY, ArrayUtils.nullToEmpty((short[]) null)); + // Test valid array handling + short[] original = new short[] {1, 2}; + assertEquals(original, ArrayUtils.nullToEmpty(original)); + // Test empty array handling + short[] empty = new short[]{}; + short[] result = ArrayUtils.nullToEmpty(empty); + assertEquals(ArrayUtils.EMPTY_SHORT_ARRAY, result); + assertTrue(empty != result); + } + + public void testNullToEmptyChar() { + // Test null handling + assertEquals(ArrayUtils.EMPTY_CHAR_ARRAY, ArrayUtils.nullToEmpty((char[]) null)); + // Test valid array handling + char[] original = new char[] {'a', 'b'}; + assertEquals(original, ArrayUtils.nullToEmpty(original)); + // Test empty array handling + char[] empty = new char[]{}; + char[] result = ArrayUtils.nullToEmpty(empty); + assertEquals(ArrayUtils.EMPTY_CHAR_ARRAY, result); + assertTrue(empty != result); + } + + public void testNullToEmptyByte() { + // Test null handling + assertEquals(ArrayUtils.EMPTY_BYTE_ARRAY, ArrayUtils.nullToEmpty((byte[]) null)); + // Test valid array handling + byte[] original = new byte[] {0x0F, 0x0E}; + assertEquals(original, ArrayUtils.nullToEmpty(original)); + // Test empty array handling + byte[] empty = new byte[]{}; + byte[] result = ArrayUtils.nullToEmpty(empty); + assertEquals(ArrayUtils.EMPTY_BYTE_ARRAY, result); + assertTrue(empty != result); + } + + public void testNullToEmptyDouble() { + // Test null handling + assertEquals(ArrayUtils.EMPTY_DOUBLE_ARRAY, ArrayUtils.nullToEmpty((double[]) null)); + // Test valid array handling + double[] original = new double[] {1L, 2L}; + assertEquals(original, ArrayUtils.nullToEmpty(original)); + // Test empty array handling + double[] empty = new double[]{}; + double[] result = ArrayUtils.nullToEmpty(empty); + assertEquals(ArrayUtils.EMPTY_DOUBLE_ARRAY, result); + assertTrue(empty != result); + } + + public void testNullToEmptyFloat() { + // Test null handling + assertEquals(ArrayUtils.EMPTY_FLOAT_ARRAY, ArrayUtils.nullToEmpty((float[]) null)); + // Test valid array handling + float[] original = new float[] {2.6f, 3.8f}; + assertEquals(original, ArrayUtils.nullToEmpty(original)); + // Test empty array handling + float[] empty = new float[]{}; + float[] result = ArrayUtils.nullToEmpty(empty); + assertEquals(ArrayUtils.EMPTY_FLOAT_ARRAY, result); + assertTrue(empty != result); + } + + public void testNullToEmptyObject() { + // Test null handling + assertEquals(ArrayUtils.EMPTY_OBJECT_ARRAY, ArrayUtils.nullToEmpty((Object[]) null)); + // Test valid array handling + Object[] original = new Object[] {Boolean.TRUE, Boolean.FALSE}; + assertEquals(original, ArrayUtils.nullToEmpty(original)); + // Test empty array handling + Object[] empty = new Object[]{}; + Object[] result = ArrayUtils.nullToEmpty(empty); + assertEquals(ArrayUtils.EMPTY_OBJECT_ARRAY, result); + assertTrue(empty != result); + } + + public void testNullToEmptyString() { + // Test null handling + assertEquals(ArrayUtils.EMPTY_STRING_ARRAY, ArrayUtils.nullToEmpty((String[]) null)); + // Test valid array handling + String[] original = new String[] {"abc", "def"}; + assertEquals(original, ArrayUtils.nullToEmpty(original)); + // Test empty array handling + String[] empty = new String[]{}; + String[] result = ArrayUtils.nullToEmpty(empty); + assertEquals(ArrayUtils.EMPTY_STRING_ARRAY, result); + assertTrue(empty != result); + } + + public void testNullToEmptyBooleanObject() { + // Test null handling + assertEquals(ArrayUtils.EMPTY_BOOLEAN_OBJECT_ARRAY, ArrayUtils.nullToEmpty((Boolean[]) null)); + // Test valid array handling + Boolean[] original = new Boolean[] {Boolean.TRUE, Boolean.FALSE}; + assertEquals(original, ArrayUtils.nullToEmpty(original)); + // Test empty array handling + Boolean[] empty = new Boolean[]{}; + Boolean[] result = ArrayUtils.nullToEmpty(empty); + assertEquals(ArrayUtils.EMPTY_BOOLEAN_OBJECT_ARRAY, result); + assertTrue(empty != result); + } + + public void testNullToEmptyLongObject() { + // Test null handling + assertEquals(ArrayUtils.EMPTY_LONG_OBJECT_ARRAY, ArrayUtils.nullToEmpty((Long[]) null)); + // Test valid array handling + @SuppressWarnings("boxing") + Long[] original = new Long[] {1L, 2L}; + assertEquals(original, ArrayUtils.nullToEmpty(original)); + // Test empty array handling + Long[] empty = new Long[]{}; + Long[] result = ArrayUtils.nullToEmpty(empty); + assertEquals(ArrayUtils.EMPTY_LONG_OBJECT_ARRAY, result); + assertTrue(empty != result); + } + + public void testNullToEmptyIntObject() { + // Test null handling + assertEquals(ArrayUtils.EMPTY_INTEGER_OBJECT_ARRAY, ArrayUtils.nullToEmpty((Integer[]) null)); + // Test valid array handling + Integer[] original = new Integer[] {1, 2}; + assertEquals(original, ArrayUtils.nullToEmpty(original)); + // Test empty array handling + Integer[] empty = new Integer[]{}; + Integer[] result = ArrayUtils.nullToEmpty(empty); + assertEquals(ArrayUtils.EMPTY_INTEGER_OBJECT_ARRAY, result); + assertTrue(empty != result); + } + + public void testNullToEmptyShortObject() { + // Test null handling + assertEquals(ArrayUtils.EMPTY_SHORT_OBJECT_ARRAY, ArrayUtils.nullToEmpty((Short[]) null)); + // Test valid array handling + @SuppressWarnings("boxing") + Short[] original = new Short[] {1, 2}; + assertEquals(original, ArrayUtils.nullToEmpty(original)); + // Test empty array handling + Short[] empty = new Short[]{}; + Short[] result = ArrayUtils.nullToEmpty(empty); + assertEquals(ArrayUtils.EMPTY_SHORT_OBJECT_ARRAY, result); + assertTrue(empty != result); + } + + public void testNullToEmptyCharObject() { + // Test null handling + assertEquals(ArrayUtils.EMPTY_CHARACTER_OBJECT_ARRAY, ArrayUtils.nullToEmpty((Character[]) null)); + // Test valid array handling + Character[] original = new Character[] {'a', 'b'}; + assertEquals(original, ArrayUtils.nullToEmpty(original)); + // Test empty array handling + Character[] empty = new Character[]{}; + Character[] result = ArrayUtils.nullToEmpty(empty); + assertEquals(ArrayUtils.EMPTY_CHARACTER_OBJECT_ARRAY, result); + assertTrue(empty != result); + } + + public void testNullToEmptyByteObject() { + // Test null handling + assertEquals(ArrayUtils.EMPTY_BYTE_OBJECT_ARRAY, ArrayUtils.nullToEmpty((Byte[]) null)); + // Test valid array handling + Byte[] original = new Byte[] {0x0F, 0x0E}; + assertEquals(original, ArrayUtils.nullToEmpty(original)); + // Test empty array handling + Byte[] empty = new Byte[]{}; + Byte[] result = ArrayUtils.nullToEmpty(empty); + assertEquals(ArrayUtils.EMPTY_BYTE_OBJECT_ARRAY, result); + assertTrue(empty != result); + } + + public void testNullToEmptyDoubleObject() { + // Test null handling + assertEquals(ArrayUtils.EMPTY_DOUBLE_OBJECT_ARRAY, ArrayUtils.nullToEmpty((Double[]) null)); + // Test valid array handling + Double[] original = new Double[] {1D, 2D}; + assertEquals(original, ArrayUtils.nullToEmpty(original)); + // Test empty array handling + Double[] empty = new Double[]{}; + Double[] result = ArrayUtils.nullToEmpty(empty); + assertEquals(ArrayUtils.EMPTY_DOUBLE_OBJECT_ARRAY, result); + assertTrue(empty != result); + } + + public void testNullToEmptyFloatObject() { + // Test null handling + assertEquals(ArrayUtils.EMPTY_FLOAT_OBJECT_ARRAY, ArrayUtils.nullToEmpty((Float[]) null)); + // Test valid array handling + Float[] original = new Float[] {2.6f, 3.8f}; + assertEquals(original, ArrayUtils.nullToEmpty(original)); + // Test empty array handling + Float[] empty = new Float[]{}; + Float[] result = ArrayUtils.nullToEmpty(empty); + assertEquals(ArrayUtils.EMPTY_FLOAT_OBJECT_ARRAY, result); + assertTrue(empty != result); + } + + //----------------------------------------------------------------------- + + public void testSubarrayObject() { + Object[] nullArray = null; + Object[] objectArray = { "a", "b", "c", "d", "e", "f"}; + + assertEquals("0 start, mid end", "abcd", + StringUtils.join(ArrayUtils.subarray(objectArray, 0, 4))); + assertEquals("0 start, length end", "abcdef", + StringUtils.join(ArrayUtils.subarray(objectArray, 0, objectArray.length))); + assertEquals("mid start, mid end", "bcd", + StringUtils.join(ArrayUtils.subarray(objectArray, 1, 4))); + assertEquals("mid start, length end", "bcdef", + StringUtils.join(ArrayUtils.subarray(objectArray, 1, objectArray.length))); + + assertNull("null input", ArrayUtils.subarray(nullArray, 0, 3)); + assertEquals("empty array", "", + StringUtils.join(ArrayUtils.subarray(ArrayUtils.EMPTY_OBJECT_ARRAY, 1, 2))); + assertEquals("start > end", "", + StringUtils.join(ArrayUtils.subarray(objectArray, 4, 2))); + assertEquals("start == end", "", + StringUtils.join(ArrayUtils.subarray(objectArray, 3, 3))); + assertEquals("start undershoot, normal end", "abcd", + StringUtils.join(ArrayUtils.subarray(objectArray, -2, 4))); + assertEquals("start overshoot, any end", "", + StringUtils.join(ArrayUtils.subarray(objectArray, 33, 4))); + assertEquals("normal start, end overshoot", "cdef", + StringUtils.join(ArrayUtils.subarray(objectArray, 2, 33))); + assertEquals("start undershoot, end overshoot", "abcdef", + StringUtils.join(ArrayUtils.subarray(objectArray, -2, 12))); + + // array type tests + Date[] dateArray = { new java.sql.Date(new Date().getTime()), + new Date(), new Date(), new Date(), new Date() }; + + assertSame("Object type", Object.class, + ArrayUtils.subarray(objectArray, 2, 4).getClass().getComponentType()); + assertSame("java.util.Date type", java.util.Date.class, + ArrayUtils.subarray(dateArray, 1, 4).getClass().getComponentType()); + assertNotSame("java.sql.Date type", java.sql.Date.class, + ArrayUtils.subarray(dateArray, 1, 4).getClass().getComponentType()); + try { + @SuppressWarnings("unused") + java.sql.Date[] dummy = (java.sql.Date[])ArrayUtils.subarray(dateArray, 1,3); + fail("Invalid downcast"); + } catch (ClassCastException e) {} + } + + public void testSubarrayLong() { + long[] nullArray = null; + long[] array = { 999910, 999911, 999912, 999913, 999914, 999915 }; + long[] leftSubarray = { 999910, 999911, 999912, 999913 }; + long[] midSubarray = { 999911, 999912, 999913, 999914 }; + long[] rightSubarray = { 999912, 999913, 999914, 999915 }; + + assertTrue("0 start, mid end", + ArrayUtils.isEquals(leftSubarray, + ArrayUtils.subarray(array, 0, 4))); + + assertTrue("0 start, length end", + ArrayUtils.isEquals(array, + ArrayUtils.subarray(array, 0, array.length))); + + assertTrue("mid start, mid end", + ArrayUtils.isEquals(midSubarray, + ArrayUtils.subarray(array, 1, 5))); + + assertTrue("mid start, length end", + ArrayUtils.isEquals(rightSubarray, + ArrayUtils.subarray(array, 2, array.length))); + + + assertNull("null input", ArrayUtils.subarray(nullArray, 0, 3)); + + assertEquals("empty array", ArrayUtils.EMPTY_LONG_ARRAY, + ArrayUtils.subarray(ArrayUtils.EMPTY_LONG_ARRAY, 1, 2)); + + assertEquals("start > end", ArrayUtils.EMPTY_LONG_ARRAY, + ArrayUtils.subarray(array, 4, 2)); + + assertEquals("start == end", ArrayUtils.EMPTY_LONG_ARRAY, + ArrayUtils.subarray(array, 3, 3)); + + assertTrue("start undershoot, normal end", + ArrayUtils.isEquals(leftSubarray, + ArrayUtils.subarray(array, -2, 4))); + + assertEquals("start overshoot, any end", + ArrayUtils.EMPTY_LONG_ARRAY, + ArrayUtils.subarray(array, 33, 4)); + + assertTrue("normal start, end overshoot", + ArrayUtils.isEquals(rightSubarray, + ArrayUtils.subarray(array, 2, 33))); + + assertTrue("start undershoot, end overshoot", + ArrayUtils.isEquals(array, + ArrayUtils.subarray(array, -2, 12))); + + // empty-return tests + + assertSame("empty array, object test", + ArrayUtils.EMPTY_LONG_ARRAY, + ArrayUtils.subarray(ArrayUtils.EMPTY_LONG_ARRAY, 1, 2)); + + assertSame("start > end, object test", + ArrayUtils.EMPTY_LONG_ARRAY, + ArrayUtils.subarray(array, 4, 1)); + + assertSame("start == end, object test", + ArrayUtils.EMPTY_LONG_ARRAY, + ArrayUtils.subarray(array, 3, 3)); + + assertSame("start overshoot, any end, object test", + ArrayUtils.EMPTY_LONG_ARRAY, + ArrayUtils.subarray(array, 8733, 4)); + + // array type tests + + assertSame("long type", long.class, + ArrayUtils.subarray(array, 2, 4).getClass().getComponentType()); + + } + + public void testSubarrayInt() { + int[] nullArray = null; + int[] array = { 10, 11, 12, 13, 14, 15 }; + int[] leftSubarray = { 10, 11, 12, 13 }; + int[] midSubarray = { 11, 12, 13, 14 }; + int[] rightSubarray = { 12, 13, 14, 15 }; + + + assertTrue("0 start, mid end", + ArrayUtils.isEquals(leftSubarray, + ArrayUtils.subarray(array, 0, 4))); + + assertTrue("0 start, length end", + ArrayUtils.isEquals(array, + ArrayUtils.subarray(array, 0, array.length))); + + assertTrue("mid start, mid end", + ArrayUtils.isEquals(midSubarray, + ArrayUtils.subarray(array, 1, 5))); + + assertTrue("mid start, length end", + ArrayUtils.isEquals(rightSubarray, + ArrayUtils.subarray(array, 2, array.length))); + + + assertNull("null input", ArrayUtils.subarray(nullArray, 0, 3)); + + assertEquals("empty array", ArrayUtils.EMPTY_INT_ARRAY, + ArrayUtils.subarray(ArrayUtils.EMPTY_INT_ARRAY, 1, 2)); + + assertEquals("start > end", ArrayUtils.EMPTY_INT_ARRAY, + ArrayUtils.subarray(array, 4, 2)); + + assertEquals("start == end", ArrayUtils.EMPTY_INT_ARRAY, + ArrayUtils.subarray(array, 3, 3)); + + assertTrue("start undershoot, normal end", + ArrayUtils.isEquals(leftSubarray, + ArrayUtils.subarray(array, -2, 4))); + + assertEquals("start overshoot, any end", + ArrayUtils.EMPTY_INT_ARRAY, + ArrayUtils.subarray(array, 33, 4)); + + assertTrue("normal start, end overshoot", + ArrayUtils.isEquals(rightSubarray, + ArrayUtils.subarray(array, 2, 33))); + + assertTrue("start undershoot, end overshoot", + ArrayUtils.isEquals(array, + ArrayUtils.subarray(array, -2, 12))); + + // empty-return tests + + assertSame("empty array, object test", + ArrayUtils.EMPTY_INT_ARRAY, + ArrayUtils.subarray(ArrayUtils.EMPTY_INT_ARRAY, 1, 2)); + + assertSame("start > end, object test", + ArrayUtils.EMPTY_INT_ARRAY, + ArrayUtils.subarray(array, 4, 1)); + + assertSame("start == end, object test", + ArrayUtils.EMPTY_INT_ARRAY, + ArrayUtils.subarray(array, 3, 3)); + + assertSame("start overshoot, any end, object test", + ArrayUtils.EMPTY_INT_ARRAY, + ArrayUtils.subarray(array, 8733, 4)); + + // array type tests + + assertSame("int type", int.class, + ArrayUtils.subarray(array, 2, 4).getClass().getComponentType()); + + } + + public void testSubarrayShort() { + short[] nullArray = null; + short[] array = { 10, 11, 12, 13, 14, 15 }; + short[] leftSubarray = { 10, 11, 12, 13 }; + short[] midSubarray = { 11, 12, 13, 14 }; + short[] rightSubarray = { 12, 13, 14, 15 }; + + + assertTrue("0 start, mid end", + ArrayUtils.isEquals(leftSubarray, + ArrayUtils.subarray(array, 0, 4))); + + assertTrue("0 start, length end", + ArrayUtils.isEquals(array, + ArrayUtils.subarray(array, 0, array.length))); + + assertTrue("mid start, mid end", + ArrayUtils.isEquals(midSubarray, + ArrayUtils.subarray(array, 1, 5))); + + assertTrue("mid start, length end", + ArrayUtils.isEquals(rightSubarray, + ArrayUtils.subarray(array, 2, array.length))); + + + assertNull("null input", ArrayUtils.subarray(nullArray, 0, 3)); + + assertEquals("empty array", ArrayUtils.EMPTY_SHORT_ARRAY, + ArrayUtils.subarray(ArrayUtils.EMPTY_SHORT_ARRAY, 1, 2)); + + assertEquals("start > end", ArrayUtils.EMPTY_SHORT_ARRAY, + ArrayUtils.subarray(array, 4, 2)); + + assertEquals("start == end", ArrayUtils.EMPTY_SHORT_ARRAY, + ArrayUtils.subarray(array, 3, 3)); + + assertTrue("start undershoot, normal end", + ArrayUtils.isEquals(leftSubarray, + ArrayUtils.subarray(array, -2, 4))); + + assertEquals("start overshoot, any end", + ArrayUtils.EMPTY_SHORT_ARRAY, + ArrayUtils.subarray(array, 33, 4)); + + assertTrue("normal start, end overshoot", + ArrayUtils.isEquals(rightSubarray, + ArrayUtils.subarray(array, 2, 33))); + + assertTrue("start undershoot, end overshoot", + ArrayUtils.isEquals(array, + ArrayUtils.subarray(array, -2, 12))); + + // empty-return tests + + assertSame("empty array, object test", + ArrayUtils.EMPTY_SHORT_ARRAY, + ArrayUtils.subarray(ArrayUtils.EMPTY_SHORT_ARRAY, 1, 2)); + + assertSame("start > end, object test", + ArrayUtils.EMPTY_SHORT_ARRAY, + ArrayUtils.subarray(array, 4, 1)); + + assertSame("start == end, object test", + ArrayUtils.EMPTY_SHORT_ARRAY, + ArrayUtils.subarray(array, 3, 3)); + + assertSame("start overshoot, any end, object test", + ArrayUtils.EMPTY_SHORT_ARRAY, + ArrayUtils.subarray(array, 8733, 4)); + + // array type tests + + assertSame("short type", short.class, + ArrayUtils.subarray(array, 2, 4).getClass().getComponentType()); + + } + + public void testSubarrChar() { + char[] nullArray = null; + char[] array = { 'a', 'b', 'c', 'd', 'e', 'f' }; + char[] leftSubarray = { 'a', 'b', 'c', 'd', }; + char[] midSubarray = { 'b', 'c', 'd', 'e', }; + char[] rightSubarray = { 'c', 'd', 'e', 'f', }; + + + assertTrue("0 start, mid end", + ArrayUtils.isEquals(leftSubarray, + ArrayUtils.subarray(array, 0, 4))); + + assertTrue("0 start, length end", + ArrayUtils.isEquals(array, + ArrayUtils.subarray(array, 0, array.length))); + + assertTrue("mid start, mid end", + ArrayUtils.isEquals(midSubarray, + ArrayUtils.subarray(array, 1, 5))); + + assertTrue("mid start, length end", + ArrayUtils.isEquals(rightSubarray, + ArrayUtils.subarray(array, 2, array.length))); + + + assertNull("null input", ArrayUtils.subarray(nullArray, 0, 3)); + + assertEquals("empty array", ArrayUtils.EMPTY_CHAR_ARRAY, + ArrayUtils.subarray(ArrayUtils.EMPTY_CHAR_ARRAY, 1, 2)); + + assertEquals("start > end", ArrayUtils.EMPTY_CHAR_ARRAY, + ArrayUtils.subarray(array, 4, 2)); + + assertEquals("start == end", ArrayUtils.EMPTY_CHAR_ARRAY, + ArrayUtils.subarray(array, 3, 3)); + + assertTrue("start undershoot, normal end", + ArrayUtils.isEquals(leftSubarray, + ArrayUtils.subarray(array, -2, 4))); + + assertEquals("start overshoot, any end", + ArrayUtils.EMPTY_CHAR_ARRAY, + ArrayUtils.subarray(array, 33, 4)); + + assertTrue("normal start, end overshoot", + ArrayUtils.isEquals(rightSubarray, + ArrayUtils.subarray(array, 2, 33))); + + assertTrue("start undershoot, end overshoot", + ArrayUtils.isEquals(array, + ArrayUtils.subarray(array, -2, 12))); + + // empty-return tests + + assertSame("empty array, object test", + ArrayUtils.EMPTY_CHAR_ARRAY, + ArrayUtils.subarray(ArrayUtils.EMPTY_CHAR_ARRAY, 1, 2)); + + assertSame("start > end, object test", + ArrayUtils.EMPTY_CHAR_ARRAY, + ArrayUtils.subarray(array, 4, 1)); + + assertSame("start == end, object test", + ArrayUtils.EMPTY_CHAR_ARRAY, + ArrayUtils.subarray(array, 3, 3)); + + assertSame("start overshoot, any end, object test", + ArrayUtils.EMPTY_CHAR_ARRAY, + ArrayUtils.subarray(array, 8733, 4)); + + // array type tests + + assertSame("char type", char.class, + ArrayUtils.subarray(array, 2, 4).getClass().getComponentType()); + + } + + public void testSubarrayByte() { + byte[] nullArray = null; + byte[] array = { 10, 11, 12, 13, 14, 15 }; + byte[] leftSubarray = { 10, 11, 12, 13 }; + byte[] midSubarray = { 11, 12, 13, 14 }; + byte[] rightSubarray = { 12, 13, 14, 15 }; + + + assertTrue("0 start, mid end", + ArrayUtils.isEquals(leftSubarray, + ArrayUtils.subarray(array, 0, 4))); + + assertTrue("0 start, length end", + ArrayUtils.isEquals(array, + ArrayUtils.subarray(array, 0, array.length))); + + assertTrue("mid start, mid end", + ArrayUtils.isEquals(midSubarray, + ArrayUtils.subarray(array, 1, 5))); + + assertTrue("mid start, length end", + ArrayUtils.isEquals(rightSubarray, + ArrayUtils.subarray(array, 2, array.length))); + + + assertNull("null input", ArrayUtils.subarray(nullArray, 0, 3)); + + assertEquals("empty array", ArrayUtils.EMPTY_BYTE_ARRAY, + ArrayUtils.subarray(ArrayUtils.EMPTY_BYTE_ARRAY, 1, 2)); + + assertEquals("start > end", ArrayUtils.EMPTY_BYTE_ARRAY, + ArrayUtils.subarray(array, 4, 2)); + + assertEquals("start == end", ArrayUtils.EMPTY_BYTE_ARRAY, + ArrayUtils.subarray(array, 3, 3)); + + assertTrue("start undershoot, normal end", + ArrayUtils.isEquals(leftSubarray, + ArrayUtils.subarray(array, -2, 4))); + + assertEquals("start overshoot, any end", + ArrayUtils.EMPTY_BYTE_ARRAY, + ArrayUtils.subarray(array, 33, 4)); + + assertTrue("normal start, end overshoot", + ArrayUtils.isEquals(rightSubarray, + ArrayUtils.subarray(array, 2, 33))); + + assertTrue("start undershoot, end overshoot", + ArrayUtils.isEquals(array, + ArrayUtils.subarray(array, -2, 12))); + + // empty-return tests + + assertSame("empty array, object test", + ArrayUtils.EMPTY_BYTE_ARRAY, + ArrayUtils.subarray(ArrayUtils.EMPTY_BYTE_ARRAY, 1, 2)); + + assertSame("start > end, object test", + ArrayUtils.EMPTY_BYTE_ARRAY, + ArrayUtils.subarray(array, 4, 1)); + + assertSame("start == end, object test", + ArrayUtils.EMPTY_BYTE_ARRAY, + ArrayUtils.subarray(array, 3, 3)); + + assertSame("start overshoot, any end, object test", + ArrayUtils.EMPTY_BYTE_ARRAY, + ArrayUtils.subarray(array, 8733, 4)); + + // array type tests + + assertSame("byte type", byte.class, + ArrayUtils.subarray(array, 2, 4).getClass().getComponentType()); + + } + + public void testSubarrayDouble() { + double[] nullArray = null; + double[] array = { 10.123, 11.234, 12.345, 13.456, 14.567, 15.678 }; + double[] leftSubarray = { 10.123, 11.234, 12.345, 13.456, }; + double[] midSubarray = { 11.234, 12.345, 13.456, 14.567, }; + double[] rightSubarray = { 12.345, 13.456, 14.567, 15.678 }; + + + assertTrue("0 start, mid end", + ArrayUtils.isEquals(leftSubarray, + ArrayUtils.subarray(array, 0, 4))); + + assertTrue("0 start, length end", + ArrayUtils.isEquals(array, + ArrayUtils.subarray(array, 0, array.length))); + + assertTrue("mid start, mid end", + ArrayUtils.isEquals(midSubarray, + ArrayUtils.subarray(array, 1, 5))); + + assertTrue("mid start, length end", + ArrayUtils.isEquals(rightSubarray, + ArrayUtils.subarray(array, 2, array.length))); + + + assertNull("null input", ArrayUtils.subarray(nullArray, 0, 3)); + + assertEquals("empty array", ArrayUtils.EMPTY_DOUBLE_ARRAY, + ArrayUtils.subarray(ArrayUtils.EMPTY_DOUBLE_ARRAY, 1, 2)); + + assertEquals("start > end", ArrayUtils.EMPTY_DOUBLE_ARRAY, + ArrayUtils.subarray(array, 4, 2)); + + assertEquals("start == end", ArrayUtils.EMPTY_DOUBLE_ARRAY, + ArrayUtils.subarray(array, 3, 3)); + + assertTrue("start undershoot, normal end", + ArrayUtils.isEquals(leftSubarray, + ArrayUtils.subarray(array, -2, 4))); + + assertEquals("start overshoot, any end", + ArrayUtils.EMPTY_DOUBLE_ARRAY, + ArrayUtils.subarray(array, 33, 4)); + + assertTrue("normal start, end overshoot", + ArrayUtils.isEquals(rightSubarray, + ArrayUtils.subarray(array, 2, 33))); + + assertTrue("start undershoot, end overshoot", + ArrayUtils.isEquals(array, + ArrayUtils.subarray(array, -2, 12))); + + // empty-return tests + + assertSame("empty array, object test", + ArrayUtils.EMPTY_DOUBLE_ARRAY, + ArrayUtils.subarray(ArrayUtils.EMPTY_DOUBLE_ARRAY, 1, 2)); + + assertSame("start > end, object test", + ArrayUtils.EMPTY_DOUBLE_ARRAY, + ArrayUtils.subarray(array, 4, 1)); + + assertSame("start == end, object test", + ArrayUtils.EMPTY_DOUBLE_ARRAY, + ArrayUtils.subarray(array, 3, 3)); + + assertSame("start overshoot, any end, object test", + ArrayUtils.EMPTY_DOUBLE_ARRAY, + ArrayUtils.subarray(array, 8733, 4)); + + // array type tests + + assertSame("double type", double.class, + ArrayUtils.subarray(array, 2, 4).getClass().getComponentType()); + + } + + public void testSubarrayFloat() { + float[] nullArray = null; + float[] array = { 10, 11, 12, 13, 14, 15 }; + float[] leftSubarray = { 10, 11, 12, 13 }; + float[] midSubarray = { 11, 12, 13, 14 }; + float[] rightSubarray = { 12, 13, 14, 15 }; + + + assertTrue("0 start, mid end", + ArrayUtils.isEquals(leftSubarray, + ArrayUtils.subarray(array, 0, 4))); + + assertTrue("0 start, length end", + ArrayUtils.isEquals(array, + ArrayUtils.subarray(array, 0, array.length))); + + assertTrue("mid start, mid end", + ArrayUtils.isEquals(midSubarray, + ArrayUtils.subarray(array, 1, 5))); + + assertTrue("mid start, length end", + ArrayUtils.isEquals(rightSubarray, + ArrayUtils.subarray(array, 2, array.length))); + + + assertNull("null input", ArrayUtils.subarray(nullArray, 0, 3)); + + assertEquals("empty array", ArrayUtils.EMPTY_FLOAT_ARRAY, + ArrayUtils.subarray(ArrayUtils.EMPTY_FLOAT_ARRAY, 1, 2)); + + assertEquals("start > end", ArrayUtils.EMPTY_FLOAT_ARRAY, + ArrayUtils.subarray(array, 4, 2)); + + assertEquals("start == end", ArrayUtils.EMPTY_FLOAT_ARRAY, + ArrayUtils.subarray(array, 3, 3)); + + assertTrue("start undershoot, normal end", + ArrayUtils.isEquals(leftSubarray, + ArrayUtils.subarray(array, -2, 4))); + + assertEquals("start overshoot, any end", + ArrayUtils.EMPTY_FLOAT_ARRAY, + ArrayUtils.subarray(array, 33, 4)); + + assertTrue("normal start, end overshoot", + ArrayUtils.isEquals(rightSubarray, + ArrayUtils.subarray(array, 2, 33))); + + assertTrue("start undershoot, end overshoot", + ArrayUtils.isEquals(array, + ArrayUtils.subarray(array, -2, 12))); + + // empty-return tests + + assertSame("empty array, object test", + ArrayUtils.EMPTY_FLOAT_ARRAY, + ArrayUtils.subarray(ArrayUtils.EMPTY_FLOAT_ARRAY, 1, 2)); + + assertSame("start > end, object test", + ArrayUtils.EMPTY_FLOAT_ARRAY, + ArrayUtils.subarray(array, 4, 1)); + + assertSame("start == end, object test", + ArrayUtils.EMPTY_FLOAT_ARRAY, + ArrayUtils.subarray(array, 3, 3)); + + assertSame("start overshoot, any end, object test", + ArrayUtils.EMPTY_FLOAT_ARRAY, + ArrayUtils.subarray(array, 8733, 4)); + + // array type tests + + assertSame("float type", float.class, + ArrayUtils.subarray(array, 2, 4).getClass().getComponentType()); + + } + + public void testSubarrayBoolean() { + boolean[] nullArray = null; + boolean[] array = { true, true, false, true, false, true }; + boolean[] leftSubarray = { true, true, false, true }; + boolean[] midSubarray = { true, false, true, false }; + boolean[] rightSubarray = { false, true, false, true }; + + + assertTrue("0 start, mid end", + ArrayUtils.isEquals(leftSubarray, + ArrayUtils.subarray(array, 0, 4))); + + assertTrue("0 start, length end", + ArrayUtils.isEquals(array, + ArrayUtils.subarray(array, 0, array.length))); + + assertTrue("mid start, mid end", + ArrayUtils.isEquals(midSubarray, + ArrayUtils.subarray(array, 1, 5))); + + assertTrue("mid start, length end", + ArrayUtils.isEquals(rightSubarray, + ArrayUtils.subarray(array, 2, array.length))); + + + assertNull("null input", ArrayUtils.subarray(nullArray, 0, 3)); + + assertEquals("empty array", ArrayUtils.EMPTY_BOOLEAN_ARRAY, + ArrayUtils.subarray(ArrayUtils.EMPTY_BOOLEAN_ARRAY, 1, 2)); + + assertEquals("start > end", ArrayUtils.EMPTY_BOOLEAN_ARRAY, + ArrayUtils.subarray(array, 4, 2)); + + assertEquals("start == end", ArrayUtils.EMPTY_BOOLEAN_ARRAY, + ArrayUtils.subarray(array, 3, 3)); + + assertTrue("start undershoot, normal end", + ArrayUtils.isEquals(leftSubarray, + ArrayUtils.subarray(array, -2, 4))); + + assertEquals("start overshoot, any end", + ArrayUtils.EMPTY_BOOLEAN_ARRAY, + ArrayUtils.subarray(array, 33, 4)); + + assertTrue("normal start, end overshoot", + ArrayUtils.isEquals(rightSubarray, + ArrayUtils.subarray(array, 2, 33))); + + assertTrue("start undershoot, end overshoot", + ArrayUtils.isEquals(array, + ArrayUtils.subarray(array, -2, 12))); + + // empty-return tests + + assertSame("empty array, object test", + ArrayUtils.EMPTY_BOOLEAN_ARRAY, + ArrayUtils.subarray(ArrayUtils.EMPTY_BOOLEAN_ARRAY, 1, 2)); + + assertSame("start > end, object test", + ArrayUtils.EMPTY_BOOLEAN_ARRAY, + ArrayUtils.subarray(array, 4, 1)); + + assertSame("start == end, object test", + ArrayUtils.EMPTY_BOOLEAN_ARRAY, + ArrayUtils.subarray(array, 3, 3)); + + assertSame("start overshoot, any end, object test", + ArrayUtils.EMPTY_BOOLEAN_ARRAY, + ArrayUtils.subarray(array, 8733, 4)); + + // array type tests + + assertSame("boolean type", boolean.class, + ArrayUtils.subarray(array, 2, 4).getClass().getComponentType()); + + } + + //----------------------------------------------------------------------- + public void testSameLength() { + Object[] nullArray = null; + Object[] emptyArray = new Object[0]; + Object[] oneArray = new Object[] {"pick"}; + Object[] twoArray = new Object[] {"pick", "stick"}; + + assertEquals(true, ArrayUtils.isSameLength(nullArray, nullArray)); + assertEquals(true, ArrayUtils.isSameLength(nullArray, emptyArray)); + assertEquals(false, ArrayUtils.isSameLength(nullArray, oneArray)); + assertEquals(false, ArrayUtils.isSameLength(nullArray, twoArray)); + + assertEquals(true, ArrayUtils.isSameLength(emptyArray, nullArray)); + assertEquals(true, ArrayUtils.isSameLength(emptyArray, emptyArray)); + assertEquals(false, ArrayUtils.isSameLength(emptyArray, oneArray)); + assertEquals(false, ArrayUtils.isSameLength(emptyArray, twoArray)); + + assertEquals(false, ArrayUtils.isSameLength(oneArray, nullArray)); + assertEquals(false, ArrayUtils.isSameLength(oneArray, emptyArray)); + assertEquals(true, ArrayUtils.isSameLength(oneArray, oneArray)); + assertEquals(false, ArrayUtils.isSameLength(oneArray, twoArray)); + + assertEquals(false, ArrayUtils.isSameLength(twoArray, nullArray)); + assertEquals(false, ArrayUtils.isSameLength(twoArray, emptyArray)); + assertEquals(false, ArrayUtils.isSameLength(twoArray, oneArray)); + assertEquals(true, ArrayUtils.isSameLength(twoArray, twoArray)); + } + + public void testSameLengthBoolean() { + boolean[] nullArray = null; + boolean[] emptyArray = new boolean[0]; + boolean[] oneArray = new boolean[] {true}; + boolean[] twoArray = new boolean[] {true, false}; + + assertEquals(true, ArrayUtils.isSameLength(nullArray, nullArray)); + assertEquals(true, ArrayUtils.isSameLength(nullArray, emptyArray)); + assertEquals(false, ArrayUtils.isSameLength(nullArray, oneArray)); + assertEquals(false, ArrayUtils.isSameLength(nullArray, twoArray)); + + assertEquals(true, ArrayUtils.isSameLength(emptyArray, nullArray)); + assertEquals(true, ArrayUtils.isSameLength(emptyArray, emptyArray)); + assertEquals(false, ArrayUtils.isSameLength(emptyArray, oneArray)); + assertEquals(false, ArrayUtils.isSameLength(emptyArray, twoArray)); + + assertEquals(false, ArrayUtils.isSameLength(oneArray, nullArray)); + assertEquals(false, ArrayUtils.isSameLength(oneArray, emptyArray)); + assertEquals(true, ArrayUtils.isSameLength(oneArray, oneArray)); + assertEquals(false, ArrayUtils.isSameLength(oneArray, twoArray)); + + assertEquals(false, ArrayUtils.isSameLength(twoArray, nullArray)); + assertEquals(false, ArrayUtils.isSameLength(twoArray, emptyArray)); + assertEquals(false, ArrayUtils.isSameLength(twoArray, oneArray)); + assertEquals(true, ArrayUtils.isSameLength(twoArray, twoArray)); + } + + public void testSameLengthLong() { + long[] nullArray = null; + long[] emptyArray = new long[0]; + long[] oneArray = new long[] {0L}; + long[] twoArray = new long[] {0L, 76L}; + + assertEquals(true, ArrayUtils.isSameLength(nullArray, nullArray)); + assertEquals(true, ArrayUtils.isSameLength(nullArray, emptyArray)); + assertEquals(false, ArrayUtils.isSameLength(nullArray, oneArray)); + assertEquals(false, ArrayUtils.isSameLength(nullArray, twoArray)); + + assertEquals(true, ArrayUtils.isSameLength(emptyArray, nullArray)); + assertEquals(true, ArrayUtils.isSameLength(emptyArray, emptyArray)); + assertEquals(false, ArrayUtils.isSameLength(emptyArray, oneArray)); + assertEquals(false, ArrayUtils.isSameLength(emptyArray, twoArray)); + + assertEquals(false, ArrayUtils.isSameLength(oneArray, nullArray)); + assertEquals(false, ArrayUtils.isSameLength(oneArray, emptyArray)); + assertEquals(true, ArrayUtils.isSameLength(oneArray, oneArray)); + assertEquals(false, ArrayUtils.isSameLength(oneArray, twoArray)); + + assertEquals(false, ArrayUtils.isSameLength(twoArray, nullArray)); + assertEquals(false, ArrayUtils.isSameLength(twoArray, emptyArray)); + assertEquals(false, ArrayUtils.isSameLength(twoArray, oneArray)); + assertEquals(true, ArrayUtils.isSameLength(twoArray, twoArray)); + } + + public void testSameLengthInt() { + int[] nullArray = null; + int[] emptyArray = new int[0]; + int[] oneArray = new int[] {4}; + int[] twoArray = new int[] {5, 7}; + + assertEquals(true, ArrayUtils.isSameLength(nullArray, nullArray)); + assertEquals(true, ArrayUtils.isSameLength(nullArray, emptyArray)); + assertEquals(false, ArrayUtils.isSameLength(nullArray, oneArray)); + assertEquals(false, ArrayUtils.isSameLength(nullArray, twoArray)); + + assertEquals(true, ArrayUtils.isSameLength(emptyArray, nullArray)); + assertEquals(true, ArrayUtils.isSameLength(emptyArray, emptyArray)); + assertEquals(false, ArrayUtils.isSameLength(emptyArray, oneArray)); + assertEquals(false, ArrayUtils.isSameLength(emptyArray, twoArray)); + + assertEquals(false, ArrayUtils.isSameLength(oneArray, nullArray)); + assertEquals(false, ArrayUtils.isSameLength(oneArray, emptyArray)); + assertEquals(true, ArrayUtils.isSameLength(oneArray, oneArray)); + assertEquals(false, ArrayUtils.isSameLength(oneArray, twoArray)); + + assertEquals(false, ArrayUtils.isSameLength(twoArray, nullArray)); + assertEquals(false, ArrayUtils.isSameLength(twoArray, emptyArray)); + assertEquals(false, ArrayUtils.isSameLength(twoArray, oneArray)); + assertEquals(true, ArrayUtils.isSameLength(twoArray, twoArray)); + } + + public void testSameLengthShort() { + short[] nullArray = null; + short[] emptyArray = new short[0]; + short[] oneArray = new short[] {4}; + short[] twoArray = new short[] {6, 8}; + + assertEquals(true, ArrayUtils.isSameLength(nullArray, nullArray)); + assertEquals(true, ArrayUtils.isSameLength(nullArray, emptyArray)); + assertEquals(false, ArrayUtils.isSameLength(nullArray, oneArray)); + assertEquals(false, ArrayUtils.isSameLength(nullArray, twoArray)); + + assertEquals(true, ArrayUtils.isSameLength(emptyArray, nullArray)); + assertEquals(true, ArrayUtils.isSameLength(emptyArray, emptyArray)); + assertEquals(false, ArrayUtils.isSameLength(emptyArray, oneArray)); + assertEquals(false, ArrayUtils.isSameLength(emptyArray, twoArray)); + + assertEquals(false, ArrayUtils.isSameLength(oneArray, nullArray)); + assertEquals(false, ArrayUtils.isSameLength(oneArray, emptyArray)); + assertEquals(true, ArrayUtils.isSameLength(oneArray, oneArray)); + assertEquals(false, ArrayUtils.isSameLength(oneArray, twoArray)); + + assertEquals(false, ArrayUtils.isSameLength(twoArray, nullArray)); + assertEquals(false, ArrayUtils.isSameLength(twoArray, emptyArray)); + assertEquals(false, ArrayUtils.isSameLength(twoArray, oneArray)); + assertEquals(true, ArrayUtils.isSameLength(twoArray, twoArray)); + } + + public void testSameLengthChar() { + char[] nullArray = null; + char[] emptyArray = new char[0]; + char[] oneArray = new char[] {'f'}; + char[] twoArray = new char[] {'d', 't'}; + + assertEquals(true, ArrayUtils.isSameLength(nullArray, nullArray)); + assertEquals(true, ArrayUtils.isSameLength(nullArray, emptyArray)); + assertEquals(false, ArrayUtils.isSameLength(nullArray, oneArray)); + assertEquals(false, ArrayUtils.isSameLength(nullArray, twoArray)); + + assertEquals(true, ArrayUtils.isSameLength(emptyArray, nullArray)); + assertEquals(true, ArrayUtils.isSameLength(emptyArray, emptyArray)); + assertEquals(false, ArrayUtils.isSameLength(emptyArray, oneArray)); + assertEquals(false, ArrayUtils.isSameLength(emptyArray, twoArray)); + + assertEquals(false, ArrayUtils.isSameLength(oneArray, nullArray)); + assertEquals(false, ArrayUtils.isSameLength(oneArray, emptyArray)); + assertEquals(true, ArrayUtils.isSameLength(oneArray, oneArray)); + assertEquals(false, ArrayUtils.isSameLength(oneArray, twoArray)); + + assertEquals(false, ArrayUtils.isSameLength(twoArray, nullArray)); + assertEquals(false, ArrayUtils.isSameLength(twoArray, emptyArray)); + assertEquals(false, ArrayUtils.isSameLength(twoArray, oneArray)); + assertEquals(true, ArrayUtils.isSameLength(twoArray, twoArray)); + } + + public void testSameLengthByte() { + byte[] nullArray = null; + byte[] emptyArray = new byte[0]; + byte[] oneArray = new byte[] {3}; + byte[] twoArray = new byte[] {4, 6}; + + assertEquals(true, ArrayUtils.isSameLength(nullArray, nullArray)); + assertEquals(true, ArrayUtils.isSameLength(nullArray, emptyArray)); + assertEquals(false, ArrayUtils.isSameLength(nullArray, oneArray)); + assertEquals(false, ArrayUtils.isSameLength(nullArray, twoArray)); + + assertEquals(true, ArrayUtils.isSameLength(emptyArray, nullArray)); + assertEquals(true, ArrayUtils.isSameLength(emptyArray, emptyArray)); + assertEquals(false, ArrayUtils.isSameLength(emptyArray, oneArray)); + assertEquals(false, ArrayUtils.isSameLength(emptyArray, twoArray)); + + assertEquals(false, ArrayUtils.isSameLength(oneArray, nullArray)); + assertEquals(false, ArrayUtils.isSameLength(oneArray, emptyArray)); + assertEquals(true, ArrayUtils.isSameLength(oneArray, oneArray)); + assertEquals(false, ArrayUtils.isSameLength(oneArray, twoArray)); + + assertEquals(false, ArrayUtils.isSameLength(twoArray, nullArray)); + assertEquals(false, ArrayUtils.isSameLength(twoArray, emptyArray)); + assertEquals(false, ArrayUtils.isSameLength(twoArray, oneArray)); + assertEquals(true, ArrayUtils.isSameLength(twoArray, twoArray)); + } + + public void testSameLengthDouble() { + double[] nullArray = null; + double[] emptyArray = new double[0]; + double[] oneArray = new double[] {1.3d}; + double[] twoArray = new double[] {4.5d, 6.3d}; + + assertEquals(true, ArrayUtils.isSameLength(nullArray, nullArray)); + assertEquals(true, ArrayUtils.isSameLength(nullArray, emptyArray)); + assertEquals(false, ArrayUtils.isSameLength(nullArray, oneArray)); + assertEquals(false, ArrayUtils.isSameLength(nullArray, twoArray)); + + assertEquals(true, ArrayUtils.isSameLength(emptyArray, nullArray)); + assertEquals(true, ArrayUtils.isSameLength(emptyArray, emptyArray)); + assertEquals(false, ArrayUtils.isSameLength(emptyArray, oneArray)); + assertEquals(false, ArrayUtils.isSameLength(emptyArray, twoArray)); + + assertEquals(false, ArrayUtils.isSameLength(oneArray, nullArray)); + assertEquals(false, ArrayUtils.isSameLength(oneArray, emptyArray)); + assertEquals(true, ArrayUtils.isSameLength(oneArray, oneArray)); + assertEquals(false, ArrayUtils.isSameLength(oneArray, twoArray)); + + assertEquals(false, ArrayUtils.isSameLength(twoArray, nullArray)); + assertEquals(false, ArrayUtils.isSameLength(twoArray, emptyArray)); + assertEquals(false, ArrayUtils.isSameLength(twoArray, oneArray)); + assertEquals(true, ArrayUtils.isSameLength(twoArray, twoArray)); + } + + public void testSameLengthFloat() { + float[] nullArray = null; + float[] emptyArray = new float[0]; + float[] oneArray = new float[] {2.5f}; + float[] twoArray = new float[] {6.4f, 5.8f}; + + assertEquals(true, ArrayUtils.isSameLength(nullArray, nullArray)); + assertEquals(true, ArrayUtils.isSameLength(nullArray, emptyArray)); + assertEquals(false, ArrayUtils.isSameLength(nullArray, oneArray)); + assertEquals(false, ArrayUtils.isSameLength(nullArray, twoArray)); + + assertEquals(true, ArrayUtils.isSameLength(emptyArray, nullArray)); + assertEquals(true, ArrayUtils.isSameLength(emptyArray, emptyArray)); + assertEquals(false, ArrayUtils.isSameLength(emptyArray, oneArray)); + assertEquals(false, ArrayUtils.isSameLength(emptyArray, twoArray)); + + assertEquals(false, ArrayUtils.isSameLength(oneArray, nullArray)); + assertEquals(false, ArrayUtils.isSameLength(oneArray, emptyArray)); + assertEquals(true, ArrayUtils.isSameLength(oneArray, oneArray)); + assertEquals(false, ArrayUtils.isSameLength(oneArray, twoArray)); + + assertEquals(false, ArrayUtils.isSameLength(twoArray, nullArray)); + assertEquals(false, ArrayUtils.isSameLength(twoArray, emptyArray)); + assertEquals(false, ArrayUtils.isSameLength(twoArray, oneArray)); + assertEquals(true, ArrayUtils.isSameLength(twoArray, twoArray)); + } + + //----------------------------------------------------------------------- + public void testSameType() { + try { + ArrayUtils.isSameType(null, null); + fail(); + } catch (IllegalArgumentException ex) {} + try { + ArrayUtils.isSameType(null, new Object[0]); + fail(); + } catch (IllegalArgumentException ex) {} + try { + ArrayUtils.isSameType(new Object[0], null); + fail(); + } catch (IllegalArgumentException ex) {} + + assertEquals(true, ArrayUtils.isSameType(new Object[0], new Object[0])); + assertEquals(false, ArrayUtils.isSameType(new String[0], new Object[0])); + assertEquals(true, ArrayUtils.isSameType(new String[0][0], new String[0][0])); + assertEquals(false, ArrayUtils.isSameType(new String[0], new String[0][0])); + assertEquals(false, ArrayUtils.isSameType(new String[0][0], new String[0])); + } + + //----------------------------------------------------------------------- + public void testReverse() { + StringBuffer str1 = new StringBuffer("pick"); + String str2 = "a"; + String[] str3 = new String[] {"stick"}; + String str4 = "up"; + + Object[] array = new Object[] {str1, str2, str3}; + ArrayUtils.reverse(array); + assertEquals(array[0], str3); + assertEquals(array[1], str2); + assertEquals(array[2], str1); + + array = new Object[] {str1, str2, str3, str4}; + ArrayUtils.reverse(array); + assertEquals(array[0], str4); + assertEquals(array[1], str3); + assertEquals(array[2], str2); + assertEquals(array[3], str1); + + array = null; + ArrayUtils.reverse(array); + assertEquals(null, array); + } + + public void testReverseLong() { + long[] array = new long[] {1L, 2L, 3L}; + ArrayUtils.reverse(array); + assertEquals(array[0], 3L); + assertEquals(array[1], 2L); + assertEquals(array[2], 1L); + + array = null; + ArrayUtils.reverse(array); + assertEquals(null, array); + } + + public void testReverseInt() { + int[] array = new int[] {1, 2, 3}; + ArrayUtils.reverse(array); + assertEquals(array[0], 3); + assertEquals(array[1], 2); + assertEquals(array[2], 1); + + array = null; + ArrayUtils.reverse(array); + assertEquals(null, array); + } + + public void testReverseShort() { + short[] array = new short[] {1, 2, 3}; + ArrayUtils.reverse(array); + assertEquals(array[0], 3); + assertEquals(array[1], 2); + assertEquals(array[2], 1); + + array = null; + ArrayUtils.reverse(array); + assertEquals(null, array); + } + + public void testReverseChar() { + char[] array = new char[] {'a', 'f', 'C'}; + ArrayUtils.reverse(array); + assertEquals(array[0], 'C'); + assertEquals(array[1], 'f'); + assertEquals(array[2], 'a'); + + array = null; + ArrayUtils.reverse(array); + assertEquals(null, array); + } + + public void testReverseByte() { + byte[] array = new byte[] {2, 3, 4}; + ArrayUtils.reverse(array); + assertEquals(array[0], 4); + assertEquals(array[1], 3); + assertEquals(array[2], 2); + + array = null; + ArrayUtils.reverse(array); + assertEquals(null, array); + } + + public void testReverseDouble() { + double[] array = new double[] {0.3d, 0.4d, 0.5d}; + ArrayUtils.reverse(array); + assertEquals(array[0], 0.5d, 0.0d); + assertEquals(array[1], 0.4d, 0.0d); + assertEquals(array[2], 0.3d, 0.0d); + + array = null; + ArrayUtils.reverse(array); + assertEquals(null, array); + } + + public void testReverseFloat() { + float[] array = new float[] {0.3f, 0.4f, 0.5f}; + ArrayUtils.reverse(array); + assertEquals(array[0], 0.5f, 0.0f); + assertEquals(array[1], 0.4f, 0.0f); + assertEquals(array[2], 0.3f, 0.0f); + + array = null; + ArrayUtils.reverse(array); + assertEquals(null, array); + } + + public void testReverseBoolean() { + boolean[] array = new boolean[] {false, false, true}; + ArrayUtils.reverse(array); + assertEquals(array[0], true); + assertEquals(array[1], false); + assertEquals(array[2], false); + + array = null; + ArrayUtils.reverse(array); + assertEquals(null, array); + } + + //----------------------------------------------------------------------- + public void testIndexOf() { + Object[] array = new Object[] { "0", "1", "2", "3", null, "0" }; + assertEquals(-1, ArrayUtils.indexOf(null, null)); + assertEquals(-1, ArrayUtils.indexOf(null, "0")); + assertEquals(-1, ArrayUtils.indexOf(new Object[0], "0")); + assertEquals(0, ArrayUtils.indexOf(array, "0")); + assertEquals(1, ArrayUtils.indexOf(array, "1")); + assertEquals(2, ArrayUtils.indexOf(array, "2")); + assertEquals(3, ArrayUtils.indexOf(array, "3")); + assertEquals(4, ArrayUtils.indexOf(array, null)); + assertEquals(-1, ArrayUtils.indexOf(array, "notInArray")); + } + + public void testIndexOfWithStartIndex() { + Object[] array = new Object[] { "0", "1", "2", "3", null, "0" }; + assertEquals(-1, ArrayUtils.indexOf(null, null, 2)); + assertEquals(-1, ArrayUtils.indexOf(new Object[0], "0", 0)); + assertEquals(-1, ArrayUtils.indexOf(null, "0", 2)); + assertEquals(5, ArrayUtils.indexOf(array, "0", 2)); + assertEquals(-1, ArrayUtils.indexOf(array, "1", 2)); + assertEquals(2, ArrayUtils.indexOf(array, "2", 2)); + assertEquals(3, ArrayUtils.indexOf(array, "3", 2)); + assertEquals(4, ArrayUtils.indexOf(array, null, 2)); + assertEquals(-1, ArrayUtils.indexOf(array, "notInArray", 2)); + + assertEquals(4, ArrayUtils.indexOf(array, null, -1)); + assertEquals(-1, ArrayUtils.indexOf(array, null, 8)); + assertEquals(-1, ArrayUtils.indexOf(array, "0", 8)); + } + + public void testLastIndexOf() { + Object[] array = new Object[] { "0", "1", "2", "3", null, "0" }; + assertEquals(-1, ArrayUtils.lastIndexOf(null, null)); + assertEquals(-1, ArrayUtils.lastIndexOf(null, "0")); + assertEquals(5, ArrayUtils.lastIndexOf(array, "0")); + assertEquals(1, ArrayUtils.lastIndexOf(array, "1")); + assertEquals(2, ArrayUtils.lastIndexOf(array, "2")); + assertEquals(3, ArrayUtils.lastIndexOf(array, "3")); + assertEquals(4, ArrayUtils.lastIndexOf(array, null)); + assertEquals(-1, ArrayUtils.lastIndexOf(array, "notInArray")); + } + + public void testLastIndexOfWithStartIndex() { + Object[] array = new Object[] { "0", "1", "2", "3", null, "0" }; + assertEquals(-1, ArrayUtils.lastIndexOf(null, null, 2)); + assertEquals(-1, ArrayUtils.lastIndexOf(null, "0", 2)); + assertEquals(0, ArrayUtils.lastIndexOf(array, "0", 2)); + assertEquals(1, ArrayUtils.lastIndexOf(array, "1", 2)); + assertEquals(2, ArrayUtils.lastIndexOf(array, "2", 2)); + assertEquals(-1, ArrayUtils.lastIndexOf(array, "3", 2)); + assertEquals(-1, ArrayUtils.lastIndexOf(array, "3", -1)); + assertEquals(4, ArrayUtils.lastIndexOf(array, null, 5)); + assertEquals(-1, ArrayUtils.lastIndexOf(array, null, 2)); + assertEquals(-1, ArrayUtils.lastIndexOf(array, "notInArray", 5)); + + assertEquals(-1, ArrayUtils.lastIndexOf(array, null, -1)); + assertEquals(5, ArrayUtils.lastIndexOf(array, "0", 88)); + } + + public void testContains() { + Object[] array = new Object[] { "0", "1", "2", "3", null, "0" }; + assertEquals(false, ArrayUtils.contains(null, null)); + assertEquals(false, ArrayUtils.contains(null, "1")); + assertEquals(true, ArrayUtils.contains(array, "0")); + assertEquals(true, ArrayUtils.contains(array, "1")); + assertEquals(true, ArrayUtils.contains(array, "2")); + assertEquals(true, ArrayUtils.contains(array, "3")); + assertEquals(true, ArrayUtils.contains(array, null)); + assertEquals(false, ArrayUtils.contains(array, "notInArray")); + } + + //----------------------------------------------------------------------- + public void testIndexOfLong() { + long[] array = null; + assertEquals(-1, ArrayUtils.indexOf(array, 0)); + array = new long[] { 0, 1, 2, 3, 0 }; + assertEquals(0, ArrayUtils.indexOf(array, 0)); + assertEquals(1, ArrayUtils.indexOf(array, 1)); + assertEquals(2, ArrayUtils.indexOf(array, 2)); + assertEquals(3, ArrayUtils.indexOf(array, 3)); + assertEquals(-1, ArrayUtils.indexOf(array, 99)); + } + + public void testIndexOfLongWithStartIndex() { + long[] array = null; + assertEquals(-1, ArrayUtils.indexOf(array, 0, 2)); + array = new long[] { 0, 1, 2, 3, 0 }; + assertEquals(4, ArrayUtils.indexOf(array, 0, 2)); + assertEquals(-1, ArrayUtils.indexOf(array, 1, 2)); + assertEquals(2, ArrayUtils.indexOf(array, 2, 2)); + assertEquals(3, ArrayUtils.indexOf(array, 3, 2)); + assertEquals(3, ArrayUtils.indexOf(array, 3, -1)); + assertEquals(-1, ArrayUtils.indexOf(array, 99, 0)); + assertEquals(-1, ArrayUtils.indexOf(array, 0, 6)); + } + + public void testLastIndexOfLong() { + long[] array = null; + assertEquals(-1, ArrayUtils.lastIndexOf(array, 0)); + array = new long[] { 0, 1, 2, 3, 0 }; + assertEquals(4, ArrayUtils.lastIndexOf(array, 0)); + assertEquals(1, ArrayUtils.lastIndexOf(array, 1)); + assertEquals(2, ArrayUtils.lastIndexOf(array, 2)); + assertEquals(3, ArrayUtils.lastIndexOf(array, 3)); + assertEquals(-1, ArrayUtils.lastIndexOf(array, 99)); + } + + public void testLastIndexOfLongWithStartIndex() { + long[] array = null; + assertEquals(-1, ArrayUtils.lastIndexOf(array, 0, 2)); + array = new long[] { 0, 1, 2, 3, 0 }; + assertEquals(0, ArrayUtils.lastIndexOf(array, 0, 2)); + assertEquals(1, ArrayUtils.lastIndexOf(array, 1, 2)); + assertEquals(2, ArrayUtils.lastIndexOf(array, 2, 2)); + assertEquals(-1, ArrayUtils.lastIndexOf(array, 3, 2)); + assertEquals(-1, ArrayUtils.lastIndexOf(array, 3, -1)); + assertEquals(-1, ArrayUtils.lastIndexOf(array, 99, 4)); + assertEquals(4, ArrayUtils.lastIndexOf(array, 0, 88)); + } + + public void testContainsLong() { + long[] array = null; + assertEquals(false, ArrayUtils.contains(array, 1)); + array = new long[] { 0, 1, 2, 3, 0 }; + assertEquals(true, ArrayUtils.contains(array, 0)); + assertEquals(true, ArrayUtils.contains(array, 1)); + assertEquals(true, ArrayUtils.contains(array, 2)); + assertEquals(true, ArrayUtils.contains(array, 3)); + assertEquals(false, ArrayUtils.contains(array, 99)); + } + + //----------------------------------------------------------------------- + public void testIndexOfInt() { + int[] array = null; + assertEquals(-1, ArrayUtils.indexOf(array, 0)); + array = new int[] { 0, 1, 2, 3, 0 }; + assertEquals(0, ArrayUtils.indexOf(array, 0)); + assertEquals(1, ArrayUtils.indexOf(array, 1)); + assertEquals(2, ArrayUtils.indexOf(array, 2)); + assertEquals(3, ArrayUtils.indexOf(array, 3)); + assertEquals(-1, ArrayUtils.indexOf(array, 99)); + } + + public void testIndexOfIntWithStartIndex() { + int[] array = null; + assertEquals(-1, ArrayUtils.indexOf(array, 0, 2)); + array = new int[] { 0, 1, 2, 3, 0 }; + assertEquals(4, ArrayUtils.indexOf(array, 0, 2)); + assertEquals(-1, ArrayUtils.indexOf(array, 1, 2)); + assertEquals(2, ArrayUtils.indexOf(array, 2, 2)); + assertEquals(3, ArrayUtils.indexOf(array, 3, 2)); + assertEquals(3, ArrayUtils.indexOf(array, 3, -1)); + assertEquals(-1, ArrayUtils.indexOf(array, 99, 0)); + assertEquals(-1, ArrayUtils.indexOf(array, 0, 6)); + } + + public void testLastIndexOfInt() { + int[] array = null; + assertEquals(-1, ArrayUtils.lastIndexOf(array, 0)); + array = new int[] { 0, 1, 2, 3, 0 }; + assertEquals(4, ArrayUtils.lastIndexOf(array, 0)); + assertEquals(1, ArrayUtils.lastIndexOf(array, 1)); + assertEquals(2, ArrayUtils.lastIndexOf(array, 2)); + assertEquals(3, ArrayUtils.lastIndexOf(array, 3)); + assertEquals(-1, ArrayUtils.lastIndexOf(array, 99)); + } + + public void testLastIndexOfIntWithStartIndex() { + int[] array = null; + assertEquals(-1, ArrayUtils.lastIndexOf(array, 0, 2)); + array = new int[] { 0, 1, 2, 3, 0 }; + assertEquals(0, ArrayUtils.lastIndexOf(array, 0, 2)); + assertEquals(1, ArrayUtils.lastIndexOf(array, 1, 2)); + assertEquals(2, ArrayUtils.lastIndexOf(array, 2, 2)); + assertEquals(-1, ArrayUtils.lastIndexOf(array, 3, 2)); + assertEquals(-1, ArrayUtils.lastIndexOf(array, 3, -1)); + assertEquals(-1, ArrayUtils.lastIndexOf(array, 99)); + assertEquals(4, ArrayUtils.lastIndexOf(array, 0, 88)); + } + + public void testContainsInt() { + int[] array = null; + assertEquals(false, ArrayUtils.contains(array, 1)); + array = new int[] { 0, 1, 2, 3, 0 }; + assertEquals(true, ArrayUtils.contains(array, 0)); + assertEquals(true, ArrayUtils.contains(array, 1)); + assertEquals(true, ArrayUtils.contains(array, 2)); + assertEquals(true, ArrayUtils.contains(array, 3)); + assertEquals(false, ArrayUtils.contains(array, 99)); + } + + //----------------------------------------------------------------------- + public void testIndexOfShort() { + short[] array = null; + assertEquals(-1, ArrayUtils.indexOf(array, (short) 0)); + array = new short[] { 0, 1, 2, 3, 0 }; + assertEquals(0, ArrayUtils.indexOf(array, (short) 0)); + assertEquals(1, ArrayUtils.indexOf(array, (short) 1)); + assertEquals(2, ArrayUtils.indexOf(array, (short) 2)); + assertEquals(3, ArrayUtils.indexOf(array, (short) 3)); + assertEquals(-1, ArrayUtils.indexOf(array, (short) 99)); + } + + public void testIndexOfShortWithStartIndex() { + short[] array = null; + assertEquals(-1, ArrayUtils.indexOf(array, (short) 0, 2)); + array = new short[] { 0, 1, 2, 3, 0 }; + assertEquals(4, ArrayUtils.indexOf(array, (short) 0, 2)); + assertEquals(-1, ArrayUtils.indexOf(array, (short) 1, 2)); + assertEquals(2, ArrayUtils.indexOf(array, (short) 2, 2)); + assertEquals(3, ArrayUtils.indexOf(array, (short) 3, 2)); + assertEquals(3, ArrayUtils.indexOf(array, (short) 3, -1)); + assertEquals(-1, ArrayUtils.indexOf(array, (short) 99, 0)); + assertEquals(-1, ArrayUtils.indexOf(array, (short) 0, 6)); + } + + public void testLastIndexOfShort() { + short[] array = null; + assertEquals(-1, ArrayUtils.lastIndexOf(array, (short) 0)); + array = new short[] { 0, 1, 2, 3, 0 }; + assertEquals(4, ArrayUtils.lastIndexOf(array, (short) 0)); + assertEquals(1, ArrayUtils.lastIndexOf(array, (short) 1)); + assertEquals(2, ArrayUtils.lastIndexOf(array, (short) 2)); + assertEquals(3, ArrayUtils.lastIndexOf(array, (short) 3)); + assertEquals(-1, ArrayUtils.lastIndexOf(array, (short) 99)); + } + + public void testLastIndexOfShortWithStartIndex() { + short[] array = null; + assertEquals(-1, ArrayUtils.lastIndexOf(array, (short) 0, 2)); + array = new short[] { 0, 1, 2, 3, 0 }; + assertEquals(0, ArrayUtils.lastIndexOf(array, (short) 0, 2)); + assertEquals(1, ArrayUtils.lastIndexOf(array, (short) 1, 2)); + assertEquals(2, ArrayUtils.lastIndexOf(array, (short) 2, 2)); + assertEquals(-1, ArrayUtils.lastIndexOf(array, (short) 3, 2)); + assertEquals(-1, ArrayUtils.lastIndexOf(array, (short) 3, -1)); + assertEquals(-1, ArrayUtils.lastIndexOf(array, (short) 99)); + assertEquals(4, ArrayUtils.lastIndexOf(array, (short) 0, 88)); + } + + public void testContainsShort() { + short[] array = null; + assertEquals(false, ArrayUtils.contains(array, (short) 1)); + array = new short[] { 0, 1, 2, 3, 0 }; + assertEquals(true, ArrayUtils.contains(array, (short) 0)); + assertEquals(true, ArrayUtils.contains(array, (short) 1)); + assertEquals(true, ArrayUtils.contains(array, (short) 2)); + assertEquals(true, ArrayUtils.contains(array, (short) 3)); + assertEquals(false, ArrayUtils.contains(array, (short) 99)); + } + + //----------------------------------------------------------------------- + public void testIndexOfChar() { + char[] array = null; + assertEquals(-1, ArrayUtils.indexOf(array, 'a')); + array = new char[] { 'a', 'b', 'c', 'd', 'a' }; + assertEquals(0, ArrayUtils.indexOf(array, 'a')); + assertEquals(1, ArrayUtils.indexOf(array, 'b')); + assertEquals(2, ArrayUtils.indexOf(array, 'c')); + assertEquals(3, ArrayUtils.indexOf(array, 'd')); + assertEquals(-1, ArrayUtils.indexOf(array, 'e')); + } + + public void testIndexOfCharWithStartIndex() { + char[] array = null; + assertEquals(-1, ArrayUtils.indexOf(array, 'a', 2)); + array = new char[] { 'a', 'b', 'c', 'd', 'a' }; + assertEquals(4, ArrayUtils.indexOf(array, 'a', 2)); + assertEquals(-1, ArrayUtils.indexOf(array, 'b', 2)); + assertEquals(2, ArrayUtils.indexOf(array, 'c', 2)); + assertEquals(3, ArrayUtils.indexOf(array, 'd', 2)); + assertEquals(3, ArrayUtils.indexOf(array, 'd', -1)); + assertEquals(-1, ArrayUtils.indexOf(array, 'e', 0)); + assertEquals(-1, ArrayUtils.indexOf(array, 'a', 6)); + } + + public void testLastIndexOfChar() { + char[] array = null; + assertEquals(-1, ArrayUtils.lastIndexOf(array, 'a')); + array = new char[] { 'a', 'b', 'c', 'd', 'a' }; + assertEquals(4, ArrayUtils.lastIndexOf(array, 'a')); + assertEquals(1, ArrayUtils.lastIndexOf(array, 'b')); + assertEquals(2, ArrayUtils.lastIndexOf(array, 'c')); + assertEquals(3, ArrayUtils.lastIndexOf(array, 'd')); + assertEquals(-1, ArrayUtils.lastIndexOf(array, 'e')); + } + + public void testLastIndexOfCharWithStartIndex() { + char[] array = null; + assertEquals(-1, ArrayUtils.lastIndexOf(array, 'a', 2)); + array = new char[] { 'a', 'b', 'c', 'd', 'a' }; + assertEquals(0, ArrayUtils.lastIndexOf(array, 'a', 2)); + assertEquals(1, ArrayUtils.lastIndexOf(array, 'b', 2)); + assertEquals(2, ArrayUtils.lastIndexOf(array, 'c', 2)); + assertEquals(-1, ArrayUtils.lastIndexOf(array, 'd', 2)); + assertEquals(-1, ArrayUtils.lastIndexOf(array, 'd', -1)); + assertEquals(-1, ArrayUtils.lastIndexOf(array, 'e')); + assertEquals(4, ArrayUtils.lastIndexOf(array, 'a', 88)); + } + + public void testContainsChar() { + char[] array = null; + assertEquals(false, ArrayUtils.contains(array, 'b')); + array = new char[] { 'a', 'b', 'c', 'd', 'a' }; + assertEquals(true, ArrayUtils.contains(array, 'a')); + assertEquals(true, ArrayUtils.contains(array, 'b')); + assertEquals(true, ArrayUtils.contains(array, 'c')); + assertEquals(true, ArrayUtils.contains(array, 'd')); + assertEquals(false, ArrayUtils.contains(array, 'e')); + } + + //----------------------------------------------------------------------- + public void testIndexOfByte() { + byte[] array = null; + assertEquals(-1, ArrayUtils.indexOf(array, (byte) 0)); + array = new byte[] { 0, 1, 2, 3, 0 }; + assertEquals(0, ArrayUtils.indexOf(array, (byte) 0)); + assertEquals(1, ArrayUtils.indexOf(array, (byte) 1)); + assertEquals(2, ArrayUtils.indexOf(array, (byte) 2)); + assertEquals(3, ArrayUtils.indexOf(array, (byte) 3)); + assertEquals(-1, ArrayUtils.indexOf(array, (byte) 99)); + } + + public void testIndexOfByteWithStartIndex() { + byte[] array = null; + assertEquals(-1, ArrayUtils.indexOf(array, (byte) 0, 2)); + array = new byte[] { 0, 1, 2, 3, 0 }; + assertEquals(4, ArrayUtils.indexOf(array, (byte) 0, 2)); + assertEquals(-1, ArrayUtils.indexOf(array, (byte) 1, 2)); + assertEquals(2, ArrayUtils.indexOf(array, (byte) 2, 2)); + assertEquals(3, ArrayUtils.indexOf(array, (byte) 3, 2)); + assertEquals(3, ArrayUtils.indexOf(array, (byte) 3, -1)); + assertEquals(-1, ArrayUtils.indexOf(array, (byte) 99, 0)); + assertEquals(-1, ArrayUtils.indexOf(array, (byte) 0, 6)); + } + + public void testLastIndexOfByte() { + byte[] array = null; + assertEquals(-1, ArrayUtils.lastIndexOf(array, (byte) 0)); + array = new byte[] { 0, 1, 2, 3, 0 }; + assertEquals(4, ArrayUtils.lastIndexOf(array, (byte) 0)); + assertEquals(1, ArrayUtils.lastIndexOf(array, (byte) 1)); + assertEquals(2, ArrayUtils.lastIndexOf(array, (byte) 2)); + assertEquals(3, ArrayUtils.lastIndexOf(array, (byte) 3)); + assertEquals(-1, ArrayUtils.lastIndexOf(array, (byte) 99)); + } + + public void testLastIndexOfByteWithStartIndex() { + byte[] array = null; + assertEquals(-1, ArrayUtils.lastIndexOf(array, (byte) 0, 2)); + array = new byte[] { 0, 1, 2, 3, 0 }; + assertEquals(0, ArrayUtils.lastIndexOf(array, (byte) 0, 2)); + assertEquals(1, ArrayUtils.lastIndexOf(array, (byte) 1, 2)); + assertEquals(2, ArrayUtils.lastIndexOf(array, (byte) 2, 2)); + assertEquals(-1, ArrayUtils.lastIndexOf(array, (byte) 3, 2)); + assertEquals(-1, ArrayUtils.lastIndexOf(array, (byte) 3, -1)); + assertEquals(-1, ArrayUtils.lastIndexOf(array, (byte) 99)); + assertEquals(4, ArrayUtils.lastIndexOf(array, (byte) 0, 88)); + } + + public void testContainsByte() { + byte[] array = null; + assertEquals(false, ArrayUtils.contains(array, (byte) 1)); + array = new byte[] { 0, 1, 2, 3, 0 }; + assertEquals(true, ArrayUtils.contains(array, (byte) 0)); + assertEquals(true, ArrayUtils.contains(array, (byte) 1)); + assertEquals(true, ArrayUtils.contains(array, (byte) 2)); + assertEquals(true, ArrayUtils.contains(array, (byte) 3)); + assertEquals(false, ArrayUtils.contains(array, (byte) 99)); + } + + //----------------------------------------------------------------------- + @SuppressWarnings("cast") + public void testIndexOfDouble() { + double[] array = null; + assertEquals(-1, ArrayUtils.indexOf(array, (double) 0)); + array = new double[0]; + assertEquals(-1, ArrayUtils.indexOf(array, (double) 0)); + array = new double[] { 0, 1, 2, 3, 0 }; + assertEquals(0, ArrayUtils.indexOf(array, (double) 0)); + assertEquals(1, ArrayUtils.indexOf(array, (double) 1)); + assertEquals(2, ArrayUtils.indexOf(array, (double) 2)); + assertEquals(3, ArrayUtils.indexOf(array, (double) 3)); + assertEquals(3, ArrayUtils.indexOf(array, (double) 3, -1)); + assertEquals(-1, ArrayUtils.indexOf(array, (double) 99)); + } + + @SuppressWarnings("cast") + public void testIndexOfDoubleTolerance() { + double[] array = null; + assertEquals(-1, ArrayUtils.indexOf(array, (double) 0, (double) 0)); + array = new double[0]; + assertEquals(-1, ArrayUtils.indexOf(array, (double) 0, (double) 0)); + array = new double[] { 0, 1, 2, 3, 0 }; + assertEquals(0, ArrayUtils.indexOf(array, (double) 0, (double) 0.3)); + assertEquals(2, ArrayUtils.indexOf(array, (double) 2.2, (double) 0.35)); + assertEquals(3, ArrayUtils.indexOf(array, (double) 4.15, (double) 2.0)); + assertEquals(1, ArrayUtils.indexOf(array, (double) 1.00001324, (double) 0.0001)); + } + + @SuppressWarnings("cast") + public void testIndexOfDoubleWithStartIndex() { + double[] array = null; + assertEquals(-1, ArrayUtils.indexOf(array, (double) 0, 2)); + array = new double[0]; + assertEquals(-1, ArrayUtils.indexOf(array, (double) 0, 2)); + array = new double[] { 0, 1, 2, 3, 0 }; + assertEquals(4, ArrayUtils.indexOf(array, (double) 0, 2)); + assertEquals(-1, ArrayUtils.indexOf(array, (double) 1, 2)); + assertEquals(2, ArrayUtils.indexOf(array, (double) 2, 2)); + assertEquals(3, ArrayUtils.indexOf(array, (double) 3, 2)); + assertEquals(-1, ArrayUtils.indexOf(array, (double) 99, 0)); + assertEquals(-1, ArrayUtils.indexOf(array, (double) 0, 6)); + } + + @SuppressWarnings("cast") + public void testIndexOfDoubleWithStartIndexTolerance() { + double[] array = null; + assertEquals(-1, ArrayUtils.indexOf(array, (double) 0, 2, (double) 0)); + array = new double[0]; + assertEquals(-1, ArrayUtils.indexOf(array, (double) 0, 2, (double) 0)); + array = new double[] { 0, 1, 2, 3, 0 }; + assertEquals(-1, ArrayUtils.indexOf(array, (double) 0, 99, (double) 0.3)); + assertEquals(0, ArrayUtils.indexOf(array, (double) 0, 0, (double) 0.3)); + assertEquals(4, ArrayUtils.indexOf(array, (double) 0, 3, (double) 0.3)); + assertEquals(2, ArrayUtils.indexOf(array, (double) 2.2, 0, (double) 0.35)); + assertEquals(3, ArrayUtils.indexOf(array, (double) 4.15, 0, (double) 2.0)); + assertEquals(1, ArrayUtils.indexOf(array, (double) 1.00001324, 0, (double) 0.0001)); + assertEquals(3, ArrayUtils.indexOf(array, (double) 4.15, -1, (double) 2.0)); + assertEquals(1, ArrayUtils.indexOf(array, (double) 1.00001324, -300, (double) 0.0001)); + } + + @SuppressWarnings("cast") + public void testLastIndexOfDouble() { + double[] array = null; + assertEquals(-1, ArrayUtils.lastIndexOf(array, (double) 0)); + array = new double[0]; + assertEquals(-1, ArrayUtils.lastIndexOf(array, (double) 0)); + array = new double[] { 0, 1, 2, 3, 0 }; + assertEquals(4, ArrayUtils.lastIndexOf(array, (double) 0)); + assertEquals(1, ArrayUtils.lastIndexOf(array, (double) 1)); + assertEquals(2, ArrayUtils.lastIndexOf(array, (double) 2)); + assertEquals(3, ArrayUtils.lastIndexOf(array, (double) 3)); + assertEquals(-1, ArrayUtils.lastIndexOf(array, (double) 99)); + } + + @SuppressWarnings("cast") + public void testLastIndexOfDoubleTolerance() { + double[] array = null; + assertEquals(-1, ArrayUtils.lastIndexOf(array, (double) 0, (double) 0)); + array = new double[0]; + assertEquals(-1, ArrayUtils.lastIndexOf(array, (double) 0, (double) 0)); + array = new double[] { 0, 1, 2, 3, 0 }; + assertEquals(4, ArrayUtils.lastIndexOf(array, (double) 0, (double) 0.3)); + assertEquals(2, ArrayUtils.lastIndexOf(array, (double) 2.2, (double) 0.35)); + assertEquals(3, ArrayUtils.lastIndexOf(array, (double) 4.15, (double) 2.0)); + assertEquals(1, ArrayUtils.lastIndexOf(array, (double) 1.00001324, (double) 0.0001)); + } + + @SuppressWarnings("cast") + public void testLastIndexOfDoubleWithStartIndex() { + double[] array = null; + assertEquals(-1, ArrayUtils.lastIndexOf(array, (double) 0, 2)); + array = new double[0]; + assertEquals(-1, ArrayUtils.lastIndexOf(array, (double) 0, 2)); + array = new double[] { 0, 1, 2, 3, 0 }; + assertEquals(0, ArrayUtils.lastIndexOf(array, (double) 0, 2)); + assertEquals(1, ArrayUtils.lastIndexOf(array, (double) 1, 2)); + assertEquals(2, ArrayUtils.lastIndexOf(array, (double) 2, 2)); + assertEquals(-1, ArrayUtils.lastIndexOf(array, (double) 3, 2)); + assertEquals(-1, ArrayUtils.lastIndexOf(array, (double) 3, -1)); + assertEquals(-1, ArrayUtils.lastIndexOf(array, (double) 99)); + assertEquals(4, ArrayUtils.lastIndexOf(array, (double) 0, 88)); + } + + @SuppressWarnings("cast") + public void testLastIndexOfDoubleWithStartIndexTolerance() { + double[] array = null; + assertEquals(-1, ArrayUtils.lastIndexOf(array, (double) 0, 2, (double) 0)); + array = new double[0]; + assertEquals(-1, ArrayUtils.lastIndexOf(array, (double) 0, 2, (double) 0)); + array = new double[] { (double) 3 }; + assertEquals(-1, ArrayUtils.lastIndexOf(array, (double) 1, 0, (double) 0)); + array = new double[] { 0, 1, 2, 3, 0 }; + assertEquals(4, ArrayUtils.lastIndexOf(array, (double) 0, 99, (double) 0.3)); + assertEquals(0, ArrayUtils.lastIndexOf(array, (double) 0, 3, (double) 0.3)); + assertEquals(2, ArrayUtils.lastIndexOf(array, (double) 2.2, 3, (double) 0.35)); + assertEquals(3, ArrayUtils.lastIndexOf(array, (double) 4.15, array.length, (double) 2.0)); + assertEquals(1, ArrayUtils.lastIndexOf(array, (double) 1.00001324, array.length, (double) 0.0001)); + assertEquals(-1, ArrayUtils.lastIndexOf(array, (double) 4.15, -200, (double) 2.0)); + } + + @SuppressWarnings("cast") + public void testContainsDouble() { + double[] array = null; + assertEquals(false, ArrayUtils.contains(array, (double) 1)); + array = new double[] { 0, 1, 2, 3, 0 }; + assertEquals(true, ArrayUtils.contains(array, (double) 0)); + assertEquals(true, ArrayUtils.contains(array, (double) 1)); + assertEquals(true, ArrayUtils.contains(array, (double) 2)); + assertEquals(true, ArrayUtils.contains(array, (double) 3)); + assertEquals(false, ArrayUtils.contains(array, (double) 99)); + } + + @SuppressWarnings("cast") + public void testContainsDoubleTolerance() { + double[] array = null; + assertEquals(false, ArrayUtils.contains(array, (double) 1, (double) 0)); + array = new double[] { 0, 1, 2, 3, 0 }; + assertEquals(false, ArrayUtils.contains(array, (double) 4.0, (double) 0.33)); + assertEquals(false, ArrayUtils.contains(array, (double) 2.5, (double) 0.49)); + assertEquals(true, ArrayUtils.contains(array, (double) 2.5, (double) 0.50)); + assertEquals(true, ArrayUtils.contains(array, (double) 2.5, (double) 0.51)); + } + + //----------------------------------------------------------------------- + @SuppressWarnings("cast") + public void testIndexOfFloat() { + float[] array = null; + assertEquals(-1, ArrayUtils.indexOf(array, (float) 0)); + array = new float[0]; + assertEquals(-1, ArrayUtils.indexOf(array, (float) 0)); + array = new float[] { 0, 1, 2, 3, 0 }; + assertEquals(0, ArrayUtils.indexOf(array, (float) 0)); + assertEquals(1, ArrayUtils.indexOf(array, (float) 1)); + assertEquals(2, ArrayUtils.indexOf(array, (float) 2)); + assertEquals(3, ArrayUtils.indexOf(array, (float) 3)); + assertEquals(-1, ArrayUtils.indexOf(array, (float) 99)); + } + + @SuppressWarnings("cast") + public void testIndexOfFloatWithStartIndex() { + float[] array = null; + assertEquals(-1, ArrayUtils.indexOf(array, (float) 0, 2)); + array = new float[0]; + assertEquals(-1, ArrayUtils.indexOf(array, (float) 0, 2)); + array = new float[] { 0, 1, 2, 3, 0 }; + assertEquals(4, ArrayUtils.indexOf(array, (float) 0, 2)); + assertEquals(-1, ArrayUtils.indexOf(array, (float) 1, 2)); + assertEquals(2, ArrayUtils.indexOf(array, (float) 2, 2)); + assertEquals(3, ArrayUtils.indexOf(array, (float) 3, 2)); + assertEquals(3, ArrayUtils.indexOf(array, (float) 3, -1)); + assertEquals(-1, ArrayUtils.indexOf(array, (float) 99, 0)); + assertEquals(-1, ArrayUtils.indexOf(array, (float) 0, 6)); + } + + @SuppressWarnings("cast") + public void testLastIndexOfFloat() { + float[] array = null; + assertEquals(-1, ArrayUtils.lastIndexOf(array, (float) 0)); + array = new float[0]; + assertEquals(-1, ArrayUtils.lastIndexOf(array, (float) 0)); + array = new float[] { 0, 1, 2, 3, 0 }; + assertEquals(4, ArrayUtils.lastIndexOf(array, (float) 0)); + assertEquals(1, ArrayUtils.lastIndexOf(array, (float) 1)); + assertEquals(2, ArrayUtils.lastIndexOf(array, (float) 2)); + assertEquals(3, ArrayUtils.lastIndexOf(array, (float) 3)); + assertEquals(-1, ArrayUtils.lastIndexOf(array, (float) 99)); + } + + @SuppressWarnings("cast") + public void testLastIndexOfFloatWithStartIndex() { + float[] array = null; + assertEquals(-1, ArrayUtils.lastIndexOf(array, (float) 0, 2)); + array = new float[0]; + assertEquals(-1, ArrayUtils.lastIndexOf(array, (float) 0, 2)); + array = new float[] { 0, 1, 2, 3, 0 }; + assertEquals(0, ArrayUtils.lastIndexOf(array, (float) 0, 2)); + assertEquals(1, ArrayUtils.lastIndexOf(array, (float) 1, 2)); + assertEquals(2, ArrayUtils.lastIndexOf(array, (float) 2, 2)); + assertEquals(-1, ArrayUtils.lastIndexOf(array, (float) 3, 2)); + assertEquals(-1, ArrayUtils.lastIndexOf(array, (float) 3, -1)); + assertEquals(-1, ArrayUtils.lastIndexOf(array, (float) 99)); + assertEquals(4, ArrayUtils.lastIndexOf(array, (float) 0, 88)); + } + + @SuppressWarnings("cast") + public void testContainsFloat() { + float[] array = null; + assertEquals(false, ArrayUtils.contains(array, (float) 1)); + array = new float[] { 0, 1, 2, 3, 0 }; + assertEquals(true, ArrayUtils.contains(array, (float) 0)); + assertEquals(true, ArrayUtils.contains(array, (float) 1)); + assertEquals(true, ArrayUtils.contains(array, (float) 2)); + assertEquals(true, ArrayUtils.contains(array, (float) 3)); + assertEquals(false, ArrayUtils.contains(array, (float) 99)); + } + + //----------------------------------------------------------------------- + public void testIndexOfBoolean() { + boolean[] array = null; + assertEquals(-1, ArrayUtils.indexOf(array, true)); + array = new boolean[0]; + assertEquals(-1, ArrayUtils.indexOf(array, true)); + array = new boolean[] { true, false, true }; + assertEquals(0, ArrayUtils.indexOf(array, true)); + assertEquals(1, ArrayUtils.indexOf(array, false)); + array = new boolean[] { true, true }; + assertEquals(-1, ArrayUtils.indexOf(array, false)); + } + + public void testIndexOfBooleanWithStartIndex() { + boolean[] array = null; + assertEquals(-1, ArrayUtils.indexOf(array, true, 2)); + array = new boolean[0]; + assertEquals(-1, ArrayUtils.indexOf(array, true, 2)); + array = new boolean[] { true, false, true }; + assertEquals(2, ArrayUtils.indexOf(array, true, 1)); + assertEquals(-1, ArrayUtils.indexOf(array, false, 2)); + assertEquals(1, ArrayUtils.indexOf(array, false, 0)); + assertEquals(1, ArrayUtils.indexOf(array, false, -1)); + array = new boolean[] { true, true }; + assertEquals(-1, ArrayUtils.indexOf(array, false, 0)); + assertEquals(-1, ArrayUtils.indexOf(array, false, -1)); + } + + public void testLastIndexOfBoolean() { + boolean[] array = null; + assertEquals(-1, ArrayUtils.lastIndexOf(array, true)); + array = new boolean[0]; + assertEquals(-1, ArrayUtils.lastIndexOf(array, true)); + array = new boolean[] { true, false, true }; + assertEquals(2, ArrayUtils.lastIndexOf(array, true)); + assertEquals(1, ArrayUtils.lastIndexOf(array, false)); + array = new boolean[] { true, true }; + assertEquals(-1, ArrayUtils.lastIndexOf(array, false)); + } + + public void testLastIndexOfBooleanWithStartIndex() { + boolean[] array = null; + assertEquals(-1, ArrayUtils.lastIndexOf(array, true, 2)); + array = new boolean[0]; + assertEquals(-1, ArrayUtils.lastIndexOf(array, true, 2)); + array = new boolean[] { true, false, true }; + assertEquals(2, ArrayUtils.lastIndexOf(array, true, 2)); + assertEquals(0, ArrayUtils.lastIndexOf(array, true, 1)); + assertEquals(1, ArrayUtils.lastIndexOf(array, false, 2)); + assertEquals(-1, ArrayUtils.lastIndexOf(array, true, -1)); + array = new boolean[] { true, true }; + assertEquals(-1, ArrayUtils.lastIndexOf(array, false, 2)); + assertEquals(-1, ArrayUtils.lastIndexOf(array, true, -1)); + } + + public void testContainsBoolean() { + boolean[] array = null; + assertEquals(false, ArrayUtils.contains(array, true)); + array = new boolean[] { true, false, true }; + assertEquals(true, ArrayUtils.contains(array, true)); + assertEquals(true, ArrayUtils.contains(array, false)); + array = new boolean[] { true, true }; + assertEquals(true, ArrayUtils.contains(array, true)); + assertEquals(false, ArrayUtils.contains(array, false)); + } + + // testToPrimitive/Object for boolean + // ----------------------------------------------------------------------- + public void testToPrimitive_boolean() { + final Boolean[] b = null; + assertEquals(null, ArrayUtils.toPrimitive(b)); + assertSame(ArrayUtils.EMPTY_BOOLEAN_ARRAY, ArrayUtils.toPrimitive(new Boolean[0])); + assertTrue(Arrays.equals( + new boolean[] {true, false, true}, + ArrayUtils.toPrimitive(new Boolean[] {Boolean.TRUE, Boolean.FALSE, Boolean.TRUE})) + ); + + try { + ArrayUtils.toPrimitive(new Boolean[] {Boolean.TRUE, null}); + fail(); + } catch (NullPointerException ex) {} + } + + public void testToPrimitive_boolean_boolean() { + assertEquals(null, ArrayUtils.toPrimitive(null, false)); + assertSame(ArrayUtils.EMPTY_BOOLEAN_ARRAY, ArrayUtils.toPrimitive(new Boolean[0], false)); + assertTrue(Arrays.equals( + new boolean[] {true, false, true}, + ArrayUtils.toPrimitive(new Boolean[] {Boolean.TRUE, Boolean.FALSE, Boolean.TRUE}, false)) + ); + assertTrue(Arrays.equals( + new boolean[] {true, false, false}, + ArrayUtils.toPrimitive(new Boolean[] {Boolean.TRUE, null, Boolean.FALSE}, false)) + ); + assertTrue(Arrays.equals( + new boolean[] {true, true, false}, + ArrayUtils.toPrimitive(new Boolean[] {Boolean.TRUE, null, Boolean.FALSE}, true)) + ); + } + + public void testToObject_boolean() { + final boolean[] b = null; + assertEquals(null, ArrayUtils.toObject(b)); + assertSame(ArrayUtils.EMPTY_BOOLEAN_OBJECT_ARRAY, ArrayUtils.toObject(new boolean[0])); + assertTrue(Arrays.equals( + new Boolean[] {Boolean.TRUE, Boolean.FALSE, Boolean.TRUE}, + ArrayUtils.toObject(new boolean[] {true, false, true})) + ); + } + + // testToPrimitive/Object for byte + // ----------------------------------------------------------------------- + public void testToPrimitive_char() { + final Character[] b = null; + assertEquals(null, ArrayUtils.toPrimitive(b)); + + assertSame(ArrayUtils.EMPTY_CHAR_ARRAY, ArrayUtils.toPrimitive(new Character[0])); + + assertTrue(Arrays.equals( + new char[] {Character.MIN_VALUE, Character.MAX_VALUE, '0'}, + ArrayUtils.toPrimitive(new Character[] {new Character(Character.MIN_VALUE), + new Character(Character.MAX_VALUE), new Character('0')})) + ); + + try { + ArrayUtils.toPrimitive(new Character[] {new Character(Character.MIN_VALUE), null}); + fail(); + } catch (NullPointerException ex) {} + } + + public void testToPrimitive_char_char() { + final Character[] b = null; + assertEquals(null, ArrayUtils.toPrimitive(b, Character.MIN_VALUE)); + + assertSame(ArrayUtils.EMPTY_CHAR_ARRAY, + ArrayUtils.toPrimitive(new Character[0], (char)0)); + + assertTrue(Arrays.equals( + new char[] {Character.MIN_VALUE, Character.MAX_VALUE, '0'}, + ArrayUtils.toPrimitive(new Character[] {new Character(Character.MIN_VALUE), + new Character(Character.MAX_VALUE), new Character('0')}, + Character.MIN_VALUE)) + ); + + assertTrue(Arrays.equals( + new char[] {Character.MIN_VALUE, Character.MAX_VALUE, '0'}, + ArrayUtils.toPrimitive(new Character[] {new Character(Character.MIN_VALUE), null, + new Character('0')}, Character.MAX_VALUE)) + ); + } + + public void testToObject_char() { + final char[] b = null; + assertEquals(null, ArrayUtils.toObject(b)); + + assertSame(ArrayUtils.EMPTY_CHARACTER_OBJECT_ARRAY, + ArrayUtils.toObject(new char[0])); + + assertTrue(Arrays.equals( + new Character[] {new Character(Character.MIN_VALUE), + new Character(Character.MAX_VALUE), new Character('0')}, + ArrayUtils.toObject(new char[] {Character.MIN_VALUE, Character.MAX_VALUE, + '0'} )) + ); + } + + // testToPrimitive/Object for byte + // ----------------------------------------------------------------------- + public void testToPrimitive_byte() { + final Byte[] b = null; + assertEquals(null, ArrayUtils.toPrimitive(b)); + + assertSame(ArrayUtils.EMPTY_BYTE_ARRAY, ArrayUtils.toPrimitive(new Byte[0])); + + assertTrue(Arrays.equals( + new byte[] {Byte.MIN_VALUE, Byte.MAX_VALUE, (byte)9999999}, + ArrayUtils.toPrimitive(new Byte[] {Byte.valueOf(Byte.MIN_VALUE), + Byte.valueOf(Byte.MAX_VALUE), Byte.valueOf((byte)9999999)})) + ); + + try { + ArrayUtils.toPrimitive(new Byte[] {Byte.valueOf(Byte.MIN_VALUE), null}); + fail(); + } catch (NullPointerException ex) {} + } + + public void testToPrimitive_byte_byte() { + final Byte[] b = null; + assertEquals(null, ArrayUtils.toPrimitive(b, Byte.MIN_VALUE)); + + assertSame(ArrayUtils.EMPTY_BYTE_ARRAY, + ArrayUtils.toPrimitive(new Byte[0], (byte)1)); + + assertTrue(Arrays.equals( + new byte[] {Byte.MIN_VALUE, Byte.MAX_VALUE, (byte)9999999}, + ArrayUtils.toPrimitive(new Byte[] {Byte.valueOf(Byte.MIN_VALUE), + Byte.valueOf(Byte.MAX_VALUE), Byte.valueOf((byte)9999999)}, + Byte.MIN_VALUE)) + ); + + assertTrue(Arrays.equals( + new byte[] {Byte.MIN_VALUE, Byte.MAX_VALUE, (byte)9999999}, + ArrayUtils.toPrimitive(new Byte[] {Byte.valueOf(Byte.MIN_VALUE), null, + Byte.valueOf((byte)9999999)}, Byte.MAX_VALUE)) + ); + } + + public void testToObject_byte() { + final byte[] b = null; + assertEquals(null, ArrayUtils.toObject(b)); + + assertSame(ArrayUtils.EMPTY_BYTE_OBJECT_ARRAY, + ArrayUtils.toObject(new byte[0])); + + assertTrue(Arrays.equals( + new Byte[] {Byte.valueOf(Byte.MIN_VALUE), + Byte.valueOf(Byte.MAX_VALUE), Byte.valueOf((byte)9999999)}, + ArrayUtils.toObject(new byte[] {Byte.MIN_VALUE, Byte.MAX_VALUE, + (byte)9999999})) + ); + } + + // testToPrimitive/Object for short + // ----------------------------------------------------------------------- + public void testToPrimitive_short() { + final Short[] b = null; + assertEquals(null, ArrayUtils.toPrimitive(b)); + + assertSame(ArrayUtils.EMPTY_SHORT_ARRAY, ArrayUtils.toPrimitive(new Short[0])); + + assertTrue(Arrays.equals( + new short[] {Short.MIN_VALUE, Short.MAX_VALUE, (short)9999999}, + ArrayUtils.toPrimitive(new Short[] {Short.valueOf(Short.MIN_VALUE), + Short.valueOf(Short.MAX_VALUE), Short.valueOf((short)9999999)})) + ); + + try { + ArrayUtils.toPrimitive(new Short[] {Short.valueOf(Short.MIN_VALUE), null}); + fail(); + } catch (NullPointerException ex) {} + } + + public void testToPrimitive_short_short() { + final Short[] s = null; + assertEquals(null, ArrayUtils.toPrimitive(s, Short.MIN_VALUE)); + + assertSame(ArrayUtils.EMPTY_SHORT_ARRAY, ArrayUtils.toPrimitive(new Short[0], + Short.MIN_VALUE)); + + assertTrue(Arrays.equals( + new short[] {Short.MIN_VALUE, Short.MAX_VALUE, (short)9999999}, + ArrayUtils.toPrimitive(new Short[] {Short.valueOf(Short.MIN_VALUE), + Short.valueOf(Short.MAX_VALUE), Short.valueOf((short)9999999)}, Short.MIN_VALUE)) + ); + + assertTrue(Arrays.equals( + new short[] {Short.MIN_VALUE, Short.MAX_VALUE, (short)9999999}, + ArrayUtils.toPrimitive(new Short[] {Short.valueOf(Short.MIN_VALUE), null, + Short.valueOf((short)9999999)}, Short.MAX_VALUE)) + ); + } + + public void testToObject_short() { + final short[] b = null; + assertEquals(null, ArrayUtils.toObject(b)); + + assertSame(ArrayUtils.EMPTY_SHORT_OBJECT_ARRAY, + ArrayUtils.toObject(new short[0])); + + assertTrue(Arrays.equals( + new Short[] {Short.valueOf(Short.MIN_VALUE), Short.valueOf(Short.MAX_VALUE), + Short.valueOf((short)9999999)}, + ArrayUtils.toObject(new short[] {Short.MIN_VALUE, Short.MAX_VALUE, + (short)9999999})) + ); + } + + // testToPrimitive/Object for int + // ----------------------------------------------------------------------- + public void testToPrimitive_int() { + final Integer[] b = null; + assertEquals(null, ArrayUtils.toPrimitive(b)); + assertSame(ArrayUtils.EMPTY_INT_ARRAY, ArrayUtils.toPrimitive(new Integer[0])); + assertTrue(Arrays.equals( + new int[] {Integer.MIN_VALUE, Integer.MAX_VALUE, 9999999}, + ArrayUtils.toPrimitive(new Integer[] {Integer.valueOf(Integer.MIN_VALUE), + Integer.valueOf(Integer.MAX_VALUE), Integer.valueOf(9999999)})) + ); + + try { + ArrayUtils.toPrimitive(new Integer[] {Integer.valueOf(Integer.MIN_VALUE), null}); + fail(); + } catch (NullPointerException ex) {} + } + + public void testToPrimitive_int_int() { + final Long[] l = null; + assertEquals(null, ArrayUtils.toPrimitive(l, Integer.MIN_VALUE)); + assertSame(ArrayUtils.EMPTY_INT_ARRAY, + ArrayUtils.toPrimitive(new Integer[0], 1)); + assertTrue(Arrays.equals( + new int[] {Integer.MIN_VALUE, Integer.MAX_VALUE, 9999999}, + ArrayUtils.toPrimitive(new Integer[] {Integer.valueOf(Integer.MIN_VALUE), + Integer.valueOf(Integer.MAX_VALUE), Integer.valueOf(9999999)},1))); + assertTrue(Arrays.equals( + new int[] {Integer.MIN_VALUE, Integer.MAX_VALUE, 9999999}, + ArrayUtils.toPrimitive(new Integer[] {Integer.valueOf(Integer.MIN_VALUE), + null, Integer.valueOf(9999999)}, Integer.MAX_VALUE)) + ); + } + + public void testToPrimitive_intNull() { + Integer[] iArray = null; + assertEquals(null, ArrayUtils.toPrimitive(iArray, Integer.MIN_VALUE)); + } + + public void testToObject_int() { + final int[] b = null; + assertEquals(null, ArrayUtils.toObject(b)); + + assertSame( + ArrayUtils.EMPTY_INTEGER_OBJECT_ARRAY, + ArrayUtils.toObject(new int[0])); + + assertTrue( + Arrays.equals( + new Integer[] { + Integer.valueOf(Integer.MIN_VALUE), + Integer.valueOf(Integer.MAX_VALUE), + Integer.valueOf(9999999)}, + ArrayUtils.toObject( + new int[] { Integer.MIN_VALUE, Integer.MAX_VALUE, 9999999 }))); + } + + // testToPrimitive/Object for long + // ----------------------------------------------------------------------- + public void testToPrimitive_long() { + final Long[] b = null; + assertEquals(null, ArrayUtils.toPrimitive(b)); + + assertSame(ArrayUtils.EMPTY_LONG_ARRAY, + ArrayUtils.toPrimitive(new Long[0])); + + assertTrue(Arrays.equals( + new long[] {Long.MIN_VALUE, Long.MAX_VALUE, 9999999}, + ArrayUtils.toPrimitive(new Long[] {Long.valueOf(Long.MIN_VALUE), + Long.valueOf(Long.MAX_VALUE), Long.valueOf(9999999)})) + ); + + try { + ArrayUtils.toPrimitive(new Long[] {Long.valueOf(Long.MIN_VALUE), null}); + fail(); + } catch (NullPointerException ex) {} + } + + public void testToPrimitive_long_long() { + final Long[] l = null; + assertEquals(null, ArrayUtils.toPrimitive(l, Long.MIN_VALUE)); + + assertSame(ArrayUtils.EMPTY_LONG_ARRAY, + ArrayUtils.toPrimitive(new Long[0], 1)); + + assertTrue(Arrays.equals( + new long[] {Long.MIN_VALUE, Long.MAX_VALUE, 9999999}, + ArrayUtils.toPrimitive(new Long[] {Long.valueOf(Long.MIN_VALUE), + Long.valueOf(Long.MAX_VALUE), Long.valueOf(9999999)},1))); + + assertTrue(Arrays.equals( + new long[] {Long.MIN_VALUE, Long.MAX_VALUE, 9999999}, + ArrayUtils.toPrimitive(new Long[] {Long.valueOf(Long.MIN_VALUE), + null, Long.valueOf(9999999)}, Long.MAX_VALUE)) + ); + } + + public void testToObject_long() { + final long[] b = null; + assertEquals(null, ArrayUtils.toObject(b)); + + assertSame( + ArrayUtils.EMPTY_LONG_OBJECT_ARRAY, + ArrayUtils.toObject(new long[0])); + + assertTrue( + Arrays.equals( + new Long[] { + Long.valueOf(Long.MIN_VALUE), + Long.valueOf(Long.MAX_VALUE), + Long.valueOf(9999999)}, + ArrayUtils.toObject( + new long[] { Long.MIN_VALUE, Long.MAX_VALUE, 9999999 }))); + } + + // testToPrimitive/Object for float + // ----------------------------------------------------------------------- + public void testToPrimitive_float() { + final Float[] b = null; + assertEquals(null, ArrayUtils.toPrimitive(b)); + + assertSame(ArrayUtils.EMPTY_FLOAT_ARRAY, + ArrayUtils.toPrimitive(new Float[0])); + + assertTrue(Arrays.equals( + new float[] {Float.MIN_VALUE, Float.MAX_VALUE, 9999999}, + ArrayUtils.toPrimitive(new Float[] {Float.valueOf(Float.MIN_VALUE), + Float.valueOf(Float.MAX_VALUE), Float.valueOf(9999999)})) + ); + + try { + ArrayUtils.toPrimitive(new Float[] {Float.valueOf(Float.MIN_VALUE), null}); + fail(); + } catch (NullPointerException ex) {} + } + + public void testToPrimitive_float_float() { + final Float[] l = null; + assertEquals(null, ArrayUtils.toPrimitive(l, Float.MIN_VALUE)); + + assertSame(ArrayUtils.EMPTY_FLOAT_ARRAY, + ArrayUtils.toPrimitive(new Float[0], 1)); + + assertTrue(Arrays.equals( + new float[] {Float.MIN_VALUE, Float.MAX_VALUE, 9999999}, + ArrayUtils.toPrimitive(new Float[] {Float.valueOf(Float.MIN_VALUE), + Float.valueOf(Float.MAX_VALUE), Float.valueOf(9999999)},1))); + + assertTrue(Arrays.equals( + new float[] {Float.MIN_VALUE, Float.MAX_VALUE, 9999999}, + ArrayUtils.toPrimitive(new Float[] {Float.valueOf(Float.MIN_VALUE), + null, Float.valueOf(9999999)}, Float.MAX_VALUE)) + ); + } + + public void testToObject_float() { + final float[] b = null; + assertEquals(null, ArrayUtils.toObject(b)); + + assertSame( + ArrayUtils.EMPTY_FLOAT_OBJECT_ARRAY, + ArrayUtils.toObject(new float[0])); + + assertTrue( + Arrays.equals( + new Float[] { + Float.valueOf(Float.MIN_VALUE), + Float.valueOf(Float.MAX_VALUE), + Float.valueOf(9999999)}, + ArrayUtils.toObject( + new float[] { Float.MIN_VALUE, Float.MAX_VALUE, 9999999 }))); + } + + // testToPrimitive/Object for double + // ----------------------------------------------------------------------- + public void testToPrimitive_double() { + final Double[] b = null; + assertEquals(null, ArrayUtils.toPrimitive(b)); + + assertSame(ArrayUtils.EMPTY_DOUBLE_ARRAY, + ArrayUtils.toPrimitive(new Double[0])); + + assertTrue(Arrays.equals( + new double[] {Double.MIN_VALUE, Double.MAX_VALUE, 9999999}, + ArrayUtils.toPrimitive(new Double[] {Double.valueOf(Double.MIN_VALUE), + Double.valueOf(Double.MAX_VALUE), Double.valueOf(9999999)})) + ); + + try { + ArrayUtils.toPrimitive(new Float[] {Float.valueOf(Float.MIN_VALUE), null}); + fail(); + } catch (NullPointerException ex) {} + } + + public void testToPrimitive_double_double() { + final Double[] l = null; + assertEquals(null, ArrayUtils.toPrimitive(l, Double.MIN_VALUE)); + + assertSame(ArrayUtils.EMPTY_DOUBLE_ARRAY, + ArrayUtils.toPrimitive(new Double[0], 1)); + + assertTrue(Arrays.equals( + new double[] {Double.MIN_VALUE, Double.MAX_VALUE, 9999999}, + ArrayUtils.toPrimitive(new Double[] {Double.valueOf(Double.MIN_VALUE), + Double.valueOf(Double.MAX_VALUE), Double.valueOf(9999999)},1))); + + assertTrue(Arrays.equals( + new double[] {Double.MIN_VALUE, Double.MAX_VALUE, 9999999}, + ArrayUtils.toPrimitive(new Double[] {Double.valueOf(Double.MIN_VALUE), + null, Double.valueOf(9999999)}, Double.MAX_VALUE)) + ); + } + + public void testToObject_double() { + final double[] b = null; + assertEquals(null, ArrayUtils.toObject(b)); + + assertSame( + ArrayUtils.EMPTY_DOUBLE_OBJECT_ARRAY, + ArrayUtils.toObject(new double[0])); + + assertTrue( + Arrays.equals( + new Double[] { + Double.valueOf(Double.MIN_VALUE), + Double.valueOf(Double.MAX_VALUE), + Double.valueOf(9999999)}, + ArrayUtils.toObject( + new double[] { Double.MIN_VALUE, Double.MAX_VALUE, 9999999 }))); + } + + //----------------------------------------------------------------------- + /** + * Test for {@link ArrayUtils#isEmpty(java.lang.Object[])}. + */ + public void testIsEmptyObject() { + Object[] emptyArray = new Object[] {}; + Object[] notEmptyArray = new Object[] { new String("Value") }; + assertEquals(true, ArrayUtils.isEmpty((Object[])null)); + assertEquals(true, ArrayUtils.isEmpty(emptyArray)); + assertEquals(false, ArrayUtils.isEmpty(notEmptyArray)); + } + + /** + * Tests for {@link ArrayUtils#isEmpty(long[])}, + * {@link ArrayUtils#isEmpty(int[])}, + * {@link ArrayUtils#isEmpty(short[])}, + * {@link ArrayUtils#isEmpty(char[])}, + * {@link ArrayUtils#isEmpty(byte[])}, + * {@link ArrayUtils#isEmpty(double[])}, + * {@link ArrayUtils#isEmpty(float[])} and + * {@link ArrayUtils#isEmpty(boolean[])}. + */ + public void testIsEmptyPrimitives() { + long[] emptyLongArray = new long[] {}; + long[] notEmptyLongArray = new long[] { 1L }; + assertEquals(true, ArrayUtils.isEmpty((long[])null)); + assertEquals(true, ArrayUtils.isEmpty(emptyLongArray)); + assertEquals(false, ArrayUtils.isEmpty(notEmptyLongArray)); + + int[] emptyIntArray = new int[] {}; + int[] notEmptyIntArray = new int[] { 1 }; + assertEquals(true, ArrayUtils.isEmpty((int[])null)); + assertEquals(true, ArrayUtils.isEmpty(emptyIntArray)); + assertEquals(false, ArrayUtils.isEmpty(notEmptyIntArray)); + + short[] emptyShortArray = new short[] {}; + short[] notEmptyShortArray = new short[] { 1 }; + assertEquals(true, ArrayUtils.isEmpty((short[])null)); + assertEquals(true, ArrayUtils.isEmpty(emptyShortArray)); + assertEquals(false, ArrayUtils.isEmpty(notEmptyShortArray)); + + char[] emptyCharArray = new char[] {}; + char[] notEmptyCharArray = new char[] { 1 }; + assertEquals(true, ArrayUtils.isEmpty((char[])null)); + assertEquals(true, ArrayUtils.isEmpty(emptyCharArray)); + assertEquals(false, ArrayUtils.isEmpty(notEmptyCharArray)); + + byte[] emptyByteArray = new byte[] {}; + byte[] notEmptyByteArray = new byte[] { 1 }; + assertEquals(true, ArrayUtils.isEmpty((byte[])null)); + assertEquals(true, ArrayUtils.isEmpty(emptyByteArray)); + assertEquals(false, ArrayUtils.isEmpty(notEmptyByteArray)); + + double[] emptyDoubleArray = new double[] {}; + double[] notEmptyDoubleArray = new double[] { 1.0 }; + assertEquals(true, ArrayUtils.isEmpty((double[])null)); + assertEquals(true, ArrayUtils.isEmpty(emptyDoubleArray)); + assertEquals(false, ArrayUtils.isEmpty(notEmptyDoubleArray)); + + float[] emptyFloatArray = new float[] {}; + float[] notEmptyFloatArray = new float[] { 1.0F }; + assertEquals(true, ArrayUtils.isEmpty((float[])null)); + assertEquals(true, ArrayUtils.isEmpty(emptyFloatArray)); + assertEquals(false, ArrayUtils.isEmpty(notEmptyFloatArray)); + + boolean[] emptyBooleanArray = new boolean[] {}; + boolean[] notEmptyBooleanArray = new boolean[] { true }; + assertEquals(true, ArrayUtils.isEmpty((boolean[])null)); + assertEquals(true, ArrayUtils.isEmpty(emptyBooleanArray)); + assertEquals(false, ArrayUtils.isEmpty(notEmptyBooleanArray)); + } + + /** + * Test for {@link ArrayUtils#isNotEmpty(java.lang.Object[])}. + */ + public void testIsNotEmptyObject() { + Object[] emptyArray = new Object[] {}; + Object[] notEmptyArray = new Object[] { new String("Value") }; + assertFalse(ArrayUtils.isNotEmpty((Object[])null)); + assertFalse(ArrayUtils.isNotEmpty(emptyArray)); + assertTrue(ArrayUtils.isNotEmpty(notEmptyArray)); + } + + /** + * Tests for {@link ArrayUtils#isNotEmpty(long[])}, + * {@link ArrayUtils#isNotEmpty(int[])}, + * {@link ArrayUtils#isNotEmpty(short[])}, + * {@link ArrayUtils#isNotEmpty(char[])}, + * {@link ArrayUtils#isNotEmpty(byte[])}, + * {@link ArrayUtils#isNotEmpty(double[])}, + * {@link ArrayUtils#isNotEmpty(float[])} and + * {@link ArrayUtils#isNotEmpty(boolean[])}. + */ + public void testIsNotEmptyPrimitives() { + long[] emptyLongArray = new long[] {}; + long[] notEmptyLongArray = new long[] { 1L }; + assertFalse(ArrayUtils.isNotEmpty((long[])null)); + assertFalse(ArrayUtils.isNotEmpty(emptyLongArray)); + assertTrue(ArrayUtils.isNotEmpty(notEmptyLongArray)); + + int[] emptyIntArray = new int[] {}; + int[] notEmptyIntArray = new int[] { 1 }; + assertFalse(ArrayUtils.isNotEmpty((int[])null)); + assertFalse(ArrayUtils.isNotEmpty(emptyIntArray)); + assertTrue(ArrayUtils.isNotEmpty(notEmptyIntArray)); + + short[] emptyShortArray = new short[] {}; + short[] notEmptyShortArray = new short[] { 1 }; + assertFalse(ArrayUtils.isNotEmpty((short[])null)); + assertFalse(ArrayUtils.isNotEmpty(emptyShortArray)); + assertTrue(ArrayUtils.isNotEmpty(notEmptyShortArray)); + + char[] emptyCharArray = new char[] {}; + char[] notEmptyCharArray = new char[] { 1 }; + assertFalse(ArrayUtils.isNotEmpty((char[])null)); + assertFalse(ArrayUtils.isNotEmpty(emptyCharArray)); + assertTrue(ArrayUtils.isNotEmpty(notEmptyCharArray)); + + byte[] emptyByteArray = new byte[] {}; + byte[] notEmptyByteArray = new byte[] { 1 }; + assertFalse(ArrayUtils.isNotEmpty((byte[])null)); + assertFalse(ArrayUtils.isNotEmpty(emptyByteArray)); + assertTrue(ArrayUtils.isNotEmpty(notEmptyByteArray)); + + double[] emptyDoubleArray = new double[] {}; + double[] notEmptyDoubleArray = new double[] { 1.0 }; + assertFalse(ArrayUtils.isNotEmpty((double[])null)); + assertFalse(ArrayUtils.isNotEmpty(emptyDoubleArray)); + assertTrue(ArrayUtils.isNotEmpty(notEmptyDoubleArray)); + + float[] emptyFloatArray = new float[] {}; + float[] notEmptyFloatArray = new float[] { 1.0F }; + assertFalse(ArrayUtils.isNotEmpty((float[])null)); + assertFalse(ArrayUtils.isNotEmpty(emptyFloatArray)); + assertTrue(ArrayUtils.isNotEmpty(notEmptyFloatArray)); + + boolean[] emptyBooleanArray = new boolean[] {}; + boolean[] notEmptyBooleanArray = new boolean[] { true }; + assertFalse(ArrayUtils.isNotEmpty((boolean[])null)); + assertFalse(ArrayUtils.isNotEmpty(emptyBooleanArray)); + assertTrue(ArrayUtils.isNotEmpty(notEmptyBooleanArray)); + } + // ------------------------------------------------------------------------ + public void testGetLength() { + assertEquals(0, ArrayUtils.getLength(null)); + + Object[] emptyObjectArray = new Object[0]; + Object[] notEmptyObjectArray = new Object[] {"aValue"}; + assertEquals(0, ArrayUtils.getLength((Object[]) null)); + assertEquals(0, ArrayUtils.getLength(emptyObjectArray)); + assertEquals(1, ArrayUtils.getLength(notEmptyObjectArray)); + + int[] emptyIntArray = new int[] {}; + int[] notEmptyIntArray = new int[] { 1 }; + assertEquals(0, ArrayUtils.getLength((int[]) null)); + assertEquals(0, ArrayUtils.getLength(emptyIntArray)); + assertEquals(1, ArrayUtils.getLength(notEmptyIntArray)); + + short[] emptyShortArray = new short[] {}; + short[] notEmptyShortArray = new short[] { 1 }; + assertEquals(0, ArrayUtils.getLength((short[]) null)); + assertEquals(0, ArrayUtils.getLength(emptyShortArray)); + assertEquals(1, ArrayUtils.getLength(notEmptyShortArray)); + + char[] emptyCharArray = new char[] {}; + char[] notEmptyCharArray = new char[] { 1 }; + assertEquals(0, ArrayUtils.getLength((char[]) null)); + assertEquals(0, ArrayUtils.getLength(emptyCharArray)); + assertEquals(1, ArrayUtils.getLength(notEmptyCharArray)); + + byte[] emptyByteArray = new byte[] {}; + byte[] notEmptyByteArray = new byte[] { 1 }; + assertEquals(0, ArrayUtils.getLength((byte[]) null)); + assertEquals(0, ArrayUtils.getLength(emptyByteArray)); + assertEquals(1, ArrayUtils.getLength(notEmptyByteArray)); + + double[] emptyDoubleArray = new double[] {}; + double[] notEmptyDoubleArray = new double[] { 1.0 }; + assertEquals(0, ArrayUtils.getLength((double[]) null)); + assertEquals(0, ArrayUtils.getLength(emptyDoubleArray)); + assertEquals(1, ArrayUtils.getLength(notEmptyDoubleArray)); + + float[] emptyFloatArray = new float[] {}; + float[] notEmptyFloatArray = new float[] { 1.0F }; + assertEquals(0, ArrayUtils.getLength((float[]) null)); + assertEquals(0, ArrayUtils.getLength(emptyFloatArray)); + assertEquals(1, ArrayUtils.getLength(notEmptyFloatArray)); + + boolean[] emptyBooleanArray = new boolean[] {}; + boolean[] notEmptyBooleanArray = new boolean[] { true }; + assertEquals(0, ArrayUtils.getLength((boolean[]) null)); + assertEquals(0, ArrayUtils.getLength(emptyBooleanArray)); + assertEquals(1, ArrayUtils.getLength(notEmptyBooleanArray)); + + try { + ArrayUtils.getLength("notAnArray"); + fail("IllegalArgumentException should have been thrown"); + } catch (IllegalArgumentException e) {} + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/BitField.java b/ApacheCommonsLang/org/apache/commons/lang3/BitField.java new file mode 100644 index 0000000..f8d936d --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/BitField.java @@ -0,0 +1,283 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3; + +/** + *

Operations on bit-mapped fields.

+ * + * @since 2.0 + * @version $Id: BitField.java 1088899 2011-04-05 05:31:27Z bayard $ + */ +public class BitField { + + private final int _mask; + private final int _shift_count; + + /** + *

Creates a BitField instance.

+ * + * @param mask the mask specifying which bits apply to this + * BitField. Bits that are set in this mask are the bits + * that this BitField operates on + */ + public BitField(int mask) { + _mask = mask; + int count = 0; + int bit_pattern = mask; + + if (bit_pattern != 0) { + while ((bit_pattern & 1) == 0) { + count++; + bit_pattern >>= 1; + } + } + _shift_count = count; + } + + /** + *

Obtains the value for the specified BitField, appropriately + * shifted right.

+ * + *

Many users of a BitField will want to treat the specified + * bits as an int value, and will not want to be aware that the + * value is stored as a BitField (and so shifted left so many + * bits).

+ * + * @see #setValue(int,int) + * @param holder the int data containing the bits we're interested + * in + * @return the selected bits, shifted right appropriately + */ + public int getValue(int holder) { + return getRawValue(holder) >> _shift_count; + } + + /** + *

Obtains the value for the specified BitField, appropriately + * shifted right, as a short.

+ * + *

Many users of a BitField will want to treat the specified + * bits as an int value, and will not want to be aware that the + * value is stored as a BitField (and so shifted left so many + * bits).

+ * + * @see #setShortValue(short,short) + * @param holder the short data containing the bits we're + * interested in + * @return the selected bits, shifted right appropriately + */ + public short getShortValue(short holder) { + return (short) getValue(holder); + } + + /** + *

Obtains the value for the specified BitField, unshifted.

+ * + * @param holder the int data containing the bits we're + * interested in + * @return the selected bits + */ + public int getRawValue(int holder) { + return holder & _mask; + } + + /** + *

Obtains the value for the specified BitField, unshifted.

+ * + * @param holder the short data containing the bits we're + * interested in + * @return the selected bits + */ + public short getShortRawValue(short holder) { + return (short) getRawValue(holder); + } + + /** + *

Returns whether the field is set or not.

+ * + *

This is most commonly used for a single-bit field, which is + * often used to represent a boolean value; the results of using + * it for a multi-bit field is to determine whether *any* of its + * bits are set.

+ * + * @param holder the int data containing the bits we're interested + * in + * @return {@code true} if any of the bits are set, + * else {@code false} + */ + public boolean isSet(int holder) { + return (holder & _mask) != 0; + } + + /** + *

Returns whether all of the bits are set or not.

+ * + *

This is a stricter test than {@link #isSet(int)}, + * in that all of the bits in a multi-bit set must be set + * for this method to return {@code true}.

+ * + * @param holder the int data containing the bits we're + * interested in + * @return {@code true} if all of the bits are set, + * else {@code false} + */ + public boolean isAllSet(int holder) { + return (holder & _mask) == _mask; + } + + /** + *

Replaces the bits with new values.

+ * + * @see #getValue(int) + * @param holder the int data containing the bits we're + * interested in + * @param value the new value for the specified bits + * @return the value of holder with the bits from the value + * parameter replacing the old bits + */ + public int setValue(int holder, int value) { + return (holder & ~_mask) | ((value << _shift_count) & _mask); + } + + /** + *

Replaces the bits with new values.

+ * + * @see #getShortValue(short) + * @param holder the short data containing the bits we're + * interested in + * @param value the new value for the specified bits + * @return the value of holder with the bits from the value + * parameter replacing the old bits + */ + public short setShortValue(short holder, short value) { + return (short) setValue(holder, value); + } + + /** + *

Clears the bits.

+ * + * @param holder the int data containing the bits we're + * interested in + * @return the value of holder with the specified bits cleared + * (set to {@code 0}) + */ + public int clear(int holder) { + return holder & ~_mask; + } + + /** + *

Clears the bits.

+ * + * @param holder the short data containing the bits we're + * interested in + * @return the value of holder with the specified bits cleared + * (set to {@code 0}) + */ + public short clearShort(short holder) { + return (short) clear(holder); + } + + /** + *

Clears the bits.

+ * + * @param holder the byte data containing the bits we're + * interested in + * + * @return the value of holder with the specified bits cleared + * (set to {@code 0}) + */ + public byte clearByte(byte holder) { + return (byte) clear(holder); + } + + /** + *

Sets the bits.

+ * + * @param holder the int data containing the bits we're + * interested in + * @return the value of holder with the specified bits set + * to {@code 1} + */ + public int set(int holder) { + return holder | _mask; + } + + /** + *

Sets the bits.

+ * + * @param holder the short data containing the bits we're + * interested in + * @return the value of holder with the specified bits set + * to {@code 1} + */ + public short setShort(short holder) { + return (short) set(holder); + } + + /** + *

Sets the bits.

+ * + * @param holder the byte data containing the bits we're + * interested in + * + * @return the value of holder with the specified bits set + * to {@code 1} + */ + public byte setByte(byte holder) { + return (byte) set(holder); + } + + /** + *

Sets a boolean BitField.

+ * + * @param holder the int data containing the bits we're + * interested in + * @param flag indicating whether to set or clear the bits + * @return the value of holder with the specified bits set or + * cleared + */ + public int setBoolean(int holder, boolean flag) { + return flag ? set(holder) : clear(holder); + } + + /** + *

Sets a boolean BitField.

+ * + * @param holder the short data containing the bits we're + * interested in + * @param flag indicating whether to set or clear the bits + * @return the value of holder with the specified bits set or + * cleared + */ + public short setShortBoolean(short holder, boolean flag) { + return flag ? setShort(holder) : clearShort(holder); + } + + /** + *

Sets a boolean BitField.

+ * + * @param holder the byte data containing the bits we're + * interested in + * @param flag indicating whether to set or clear the bits + * @return the value of holder with the specified bits set or + * cleared + */ + public byte setByteBoolean(byte holder, boolean flag) { + return flag ? setByte(holder) : clearByte(holder); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/BitFieldTest.java b/ApacheCommonsLang/org/apache/commons/lang3/BitFieldTest.java new file mode 100644 index 0000000..c276894 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/BitFieldTest.java @@ -0,0 +1,251 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3; + +import junit.framework.TestCase; + +/** + * Class to test BitField functionality + * + * @version $Id: BitFieldTest.java 1199894 2011-11-09 17:53:59Z ggregory $ + */ +public class BitFieldTest extends TestCase { + + private static final BitField bf_multi = new BitField(0x3F80); + private static final BitField bf_single = new BitField(0x4000); + private static final BitField bf_zero = new BitField(0); + + /** + * Constructor BitFieldTest + * + * @param name + */ + public BitFieldTest(String name) { + super(name); + } + + /** + * test the getValue() method + */ + public void testGetValue() { + assertEquals(bf_multi.getValue(-1), 127); + assertEquals(bf_multi.getValue(0), 0); + assertEquals(bf_single.getValue(-1), 1); + assertEquals(bf_single.getValue(0), 0); + assertEquals(bf_zero.getValue(-1), 0); + assertEquals(bf_zero.getValue(0), 0); + } + + /** + * test the getShortValue() method + */ + public void testGetShortValue() { + assertEquals(bf_multi.getShortValue((short) - 1), (short) 127); + assertEquals(bf_multi.getShortValue((short) 0), (short) 0); + assertEquals(bf_single.getShortValue((short) - 1), (short) 1); + assertEquals(bf_single.getShortValue((short) 0), (short) 0); + assertEquals(bf_zero.getShortValue((short) -1), (short) 0); + assertEquals(bf_zero.getShortValue((short) 0), (short) 0); + } + + /** + * test the getRawValue() method + */ + public void testGetRawValue() { + assertEquals(bf_multi.getRawValue(-1), 0x3F80); + assertEquals(bf_multi.getRawValue(0), 0); + assertEquals(bf_single.getRawValue(-1), 0x4000); + assertEquals(bf_single.getRawValue(0), 0); + assertEquals(bf_zero.getRawValue(-1), 0); + assertEquals(bf_zero.getRawValue(0), 0); + } + + /** + * test the getShortRawValue() method + */ + public void testGetShortRawValue() { + assertEquals(bf_multi.getShortRawValue((short) - 1), (short) 0x3F80); + assertEquals(bf_multi.getShortRawValue((short) 0), (short) 0); + assertEquals(bf_single.getShortRawValue((short) - 1), (short) 0x4000); + assertEquals(bf_single.getShortRawValue((short) 0), (short) 0); + assertEquals(bf_zero.getShortRawValue((short) -1), (short) 0); + assertEquals(bf_zero.getShortRawValue((short) 0), (short) 0); + } + + /** + * test the isSet() method + */ + public void testIsSet() { + assertTrue(!bf_multi.isSet(0)); + assertTrue(!bf_zero.isSet(0)); + for (int j = 0x80; j <= 0x3F80; j += 0x80) { + assertTrue(bf_multi.isSet(j)); + } + for (int j = 0x80; j <= 0x3F80; j += 0x80) { + assertTrue(!bf_zero.isSet(j)); + } + assertTrue(!bf_single.isSet(0)); + assertTrue(bf_single.isSet(0x4000)); + } + + /** + * test the isAllSet() method + */ + public void testIsAllSet() { + for (int j = 0; j < 0x3F80; j += 0x80) { + assertTrue(!bf_multi.isAllSet(j)); + assertTrue(bf_zero.isAllSet(j)); + } + assertTrue(bf_multi.isAllSet(0x3F80)); + assertTrue(!bf_single.isAllSet(0)); + assertTrue(bf_single.isAllSet(0x4000)); + } + + /** + * test the setValue() method + */ + public void testSetValue() { + for (int j = 0; j < 128; j++) { + assertEquals(bf_multi.getValue(bf_multi.setValue(0, j)), j); + assertEquals(bf_multi.setValue(0, j), j << 7); + } + for (int j = 0; j < 128; j++) { + assertEquals(bf_zero.getValue(bf_zero.setValue(0, j)), 0); + assertEquals(bf_zero.setValue(0, j), 0); + } + + // verify that excess bits are stripped off + assertEquals(bf_multi.setValue(0x3f80, 128), 0); + for (int j = 0; j < 2; j++) { + assertEquals(bf_single.getValue(bf_single.setValue(0, j)), j); + assertEquals(bf_single.setValue(0, j), j << 14); + } + + // verify that excess bits are stripped off + assertEquals(bf_single.setValue(0x4000, 2), 0); + } + + /** + * test the setShortValue() method + */ + public void testSetShortValue() { + for (int j = 0; j < 128; j++) { + assertEquals(bf_multi.getShortValue(bf_multi.setShortValue((short) 0, (short) j)), (short) j); + assertEquals(bf_multi.setShortValue((short) 0, (short) j), (short) (j << 7)); + } + for (int j = 0; j < 128; j++) { + assertEquals(bf_zero.getShortValue(bf_zero.setShortValue((short) 0, (short) j)), (short) 0); + assertEquals(bf_zero.setShortValue((short) 0, (short) j), (short) 0); + } + + // verify that excess bits are stripped off + assertEquals(bf_multi.setShortValue((short) 0x3f80, (short) 128), (short) 0); + for (int j = 0; j < 2; j++) { + assertEquals(bf_single.getShortValue(bf_single.setShortValue((short) 0, (short) j)), (short) j); + assertEquals(bf_single.setShortValue((short) 0, (short) j), (short) (j << 14)); + } + + // verify that excess bits are stripped off + assertEquals(bf_single.setShortValue((short) 0x4000, (short) 2), (short) 0); + } + + public void testByte() { + assertEquals(0, new BitField(0).setByteBoolean((byte) 0, true)); + assertEquals(1, new BitField(1).setByteBoolean((byte) 0, true)); + assertEquals(2, new BitField(2).setByteBoolean((byte) 0, true)); + assertEquals(4, new BitField(4).setByteBoolean((byte) 0, true)); + assertEquals(8, new BitField(8).setByteBoolean((byte) 0, true)); + assertEquals(16, new BitField(16).setByteBoolean((byte) 0, true)); + assertEquals(32, new BitField(32).setByteBoolean((byte) 0, true)); + assertEquals(64, new BitField(64).setByteBoolean((byte) 0, true)); + assertEquals(-128, new BitField(128).setByteBoolean((byte) 0, true)); + assertEquals(1, new BitField(0).setByteBoolean((byte) 1, false)); + assertEquals(0, new BitField(1).setByteBoolean((byte) 1, false)); + assertEquals(0, new BitField(2).setByteBoolean((byte) 2, false)); + assertEquals(0, new BitField(4).setByteBoolean((byte) 4, false)); + assertEquals(0, new BitField(8).setByteBoolean((byte) 8, false)); + assertEquals(0, new BitField(16).setByteBoolean((byte) 16, false)); + assertEquals(0, new BitField(32).setByteBoolean((byte) 32, false)); + assertEquals(0, new BitField(64).setByteBoolean((byte) 64, false)); + assertEquals(0, new BitField(128).setByteBoolean((byte) 128, false)); + assertEquals(-2, new BitField(1).setByteBoolean((byte) 255, false)); + byte clearedBit = new BitField(0x40).setByteBoolean((byte) - 63, false); + + assertEquals(false, new BitField(0x40).isSet(clearedBit)); + } + + /** + * test the clear() method + */ + public void testClear() { + assertEquals(bf_multi.clear(-1), 0xFFFFC07F); + assertEquals(bf_single.clear(-1), 0xFFFFBFFF); + assertEquals(bf_zero.clear(-1), 0xFFFFFFFF); + } + + /** + * test the clearShort() method + */ + public void testClearShort() { + assertEquals(bf_multi.clearShort((short) - 1), (short) 0xC07F); + assertEquals(bf_single.clearShort((short) - 1), (short) 0xBFFF); + assertEquals(bf_zero.clearShort((short) -1), (short) 0xFFFF); + } + + /** + * test the set() method + */ + public void testSet() { + assertEquals(bf_multi.set(0), 0x3F80); + assertEquals(bf_single.set(0), 0x4000); + assertEquals(bf_zero.set(0), 0); + } + + /** + * test the setShort() method + */ + public void testSetShort() { + assertEquals(bf_multi.setShort((short) 0), (short) 0x3F80); + assertEquals(bf_single.setShort((short) 0), (short) 0x4000); + assertEquals(bf_zero.setShort((short) 0), (short) 0); + } + + /** + * test the setBoolean() method + */ + public void testSetBoolean() { + assertEquals(bf_multi.set(0), bf_multi.setBoolean(0, true)); + assertEquals(bf_single.set(0), bf_single.setBoolean(0, true)); + assertEquals(bf_zero.set(0), bf_zero.setBoolean(0, true)); + assertEquals(bf_multi.clear(-1), bf_multi.setBoolean(-1, false)); + assertEquals(bf_single.clear(-1), bf_single.setBoolean(-1, false)); + assertEquals(bf_zero.clear(-1), bf_zero.setBoolean(-1, false)); + } + + /** + * test the setShortBoolean() method + */ + public void testSetShortBoolean() { + assertEquals(bf_multi.setShort((short) 0), bf_multi.setShortBoolean((short) 0, true)); + assertEquals(bf_single.setShort((short) 0), bf_single.setShortBoolean((short) 0, true)); + assertEquals(bf_zero.setShort((short) 0), bf_zero.setShortBoolean((short) 0, true)); + assertEquals(bf_multi.clearShort((short) - 1), bf_multi.setShortBoolean((short) - 1, false)); + assertEquals(bf_single.clearShort((short) - 1), bf_single.setShortBoolean((short) - 1, false)); + assertEquals(bf_zero.clearShort((short) -1), bf_zero.setShortBoolean((short) -1, false)); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/BooleanUtils.java b/ApacheCommonsLang/org/apache/commons/lang3/BooleanUtils.java new file mode 100644 index 0000000..8244475 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/BooleanUtils.java @@ -0,0 +1,1082 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3; + +import org.apache.commons.lang3.math.NumberUtils; + +/** + *

Operations on boolean primitives and Boolean objects.

+ * + *

This class tries to handle {@code null} input gracefully. + * An exception will not be thrown for a {@code null} input. + * Each method documents its behaviour in more detail.

+ * + *

#ThreadSafe#

+ * @since 2.0 + * @version $Id: BooleanUtils.java 1199894 2011-11-09 17:53:59Z ggregory $ + */ +public class BooleanUtils { + + /** + *

{@code BooleanUtils} instances should NOT be constructed in standard programming. + * Instead, the class should be used as {@code BooleanUtils.negate(true);}.

+ * + *

This constructor is public to permit tools that require a JavaBean instance + * to operate.

+ */ + public BooleanUtils() { + super(); + } + + // Boolean utilities + //-------------------------------------------------------------------------- + /** + *

Negates the specified boolean.

+ * + *

If {@code null} is passed in, {@code null} will be returned.

+ * + *

NOTE: This returns null and will throw a NullPointerException if autoboxed to a boolean.

+ * + *
+     *   BooleanUtils.negate(Boolean.TRUE)  = Boolean.FALSE;
+     *   BooleanUtils.negate(Boolean.FALSE) = Boolean.TRUE;
+     *   BooleanUtils.negate(null)          = null;
+     * 
+ * + * @param bool the Boolean to negate, may be null + * @return the negated Boolean, or {@code null} if {@code null} input + */ + public static Boolean negate(Boolean bool) { + if (bool == null) { + return null; + } + return bool.booleanValue() ? Boolean.FALSE : Boolean.TRUE; + } + + // boolean Boolean methods + //----------------------------------------------------------------------- + /** + *

Checks if a {@code Boolean} value is {@code true}, + * handling {@code null} by returning {@code false}.

+ * + *
+     *   BooleanUtils.isTrue(Boolean.TRUE)  = true
+     *   BooleanUtils.isTrue(Boolean.FALSE) = false
+     *   BooleanUtils.isTrue(null)          = false
+     * 
+ * + * @param bool the boolean to check, null returns {@code false} + * @return {@code true} only if the input is non-null and true + * @since 2.1 + */ + public static boolean isTrue(Boolean bool) { + return Boolean.TRUE.equals(bool); + } + + /** + *

Checks if a {@code Boolean} value is not {@code true}, + * handling {@code null} by returning {@code true}.

+ * + *
+     *   BooleanUtils.isNotTrue(Boolean.TRUE)  = false
+     *   BooleanUtils.isNotTrue(Boolean.FALSE) = true
+     *   BooleanUtils.isNotTrue(null)          = true
+     * 
+ * + * @param bool the boolean to check, null returns {@code true} + * @return {@code true} if the input is null or false + * @since 2.3 + */ + public static boolean isNotTrue(Boolean bool) { + return !isTrue(bool); + } + + /** + *

Checks if a {@code Boolean} value is {@code false}, + * handling {@code null} by returning {@code false}.

+ * + *
+     *   BooleanUtils.isFalse(Boolean.TRUE)  = false
+     *   BooleanUtils.isFalse(Boolean.FALSE) = true
+     *   BooleanUtils.isFalse(null)          = false
+     * 
+ * + * @param bool the boolean to check, null returns {@code false} + * @return {@code true} only if the input is non-null and false + * @since 2.1 + */ + public static boolean isFalse(Boolean bool) { + return Boolean.FALSE.equals(bool); + } + + /** + *

Checks if a {@code Boolean} value is not {@code false}, + * handling {@code null} by returning {@code true}.

+ * + *
+     *   BooleanUtils.isNotFalse(Boolean.TRUE)  = true
+     *   BooleanUtils.isNotFalse(Boolean.FALSE) = false
+     *   BooleanUtils.isNotFalse(null)          = true
+     * 
+ * + * @param bool the boolean to check, null returns {@code true} + * @return {@code true} if the input is null or true + * @since 2.3 + */ + public static boolean isNotFalse(Boolean bool) { + return !isFalse(bool); + } + + //----------------------------------------------------------------------- + /** + *

Converts a Boolean to a boolean handling {@code null} + * by returning {@code false}.

+ * + *
+     *   BooleanUtils.toBoolean(Boolean.TRUE)  = true
+     *   BooleanUtils.toBoolean(Boolean.FALSE) = false
+     *   BooleanUtils.toBoolean(null)          = false
+     * 
+ * + * @param bool the boolean to convert + * @return {@code true} or {@code false}, {@code null} returns {@code false} + */ + public static boolean toBoolean(Boolean bool) { + return bool != null && bool.booleanValue(); + } + + /** + *

Converts a Boolean to a boolean handling {@code null}.

+ * + *
+     *   BooleanUtils.toBooleanDefaultIfNull(Boolean.TRUE, false) = true
+     *   BooleanUtils.toBooleanDefaultIfNull(Boolean.FALSE, true) = false
+     *   BooleanUtils.toBooleanDefaultIfNull(null, true)          = true
+     * 
+ * + * @param bool the boolean to convert + * @param valueIfNull the boolean value to return if {@code null} + * @return {@code true} or {@code false} + */ + public static boolean toBooleanDefaultIfNull(Boolean bool, boolean valueIfNull) { + if (bool == null) { + return valueIfNull; + } + return bool.booleanValue(); + } + + // Integer to Boolean methods + //----------------------------------------------------------------------- + /** + *

Converts an int to a boolean using the convention that {@code zero} + * is {@code false}.

+ * + *
+     *   BooleanUtils.toBoolean(0) = false
+     *   BooleanUtils.toBoolean(1) = true
+     *   BooleanUtils.toBoolean(2) = true
+     * 
+ * + * @param value the int to convert + * @return {@code true} if non-zero, {@code false} + * if zero + */ + public static boolean toBoolean(int value) { + return value != 0; + } + + /** + *

Converts an int to a Boolean using the convention that {@code zero} + * is {@code false}.

+ * + *
+     *   BooleanUtils.toBoolean(0) = Boolean.FALSE
+     *   BooleanUtils.toBoolean(1) = Boolean.TRUE
+     *   BooleanUtils.toBoolean(2) = Boolean.TRUE
+     * 
+ * + * @param value the int to convert + * @return Boolean.TRUE if non-zero, Boolean.FALSE if zero, + * {@code null} if {@code null} + */ + public static Boolean toBooleanObject(int value) { + return value == 0 ? Boolean.FALSE : Boolean.TRUE; + } + + /** + *

Converts an Integer to a Boolean using the convention that {@code zero} + * is {@code false}.

+ * + *

{@code null} will be converted to {@code null}.

+ * + *

NOTE: This returns null and will throw a NullPointerException if autoboxed to a boolean.

+ * + *
+     *   BooleanUtils.toBoolean(Integer.valueOf(0))    = Boolean.FALSE
+     *   BooleanUtils.toBoolean(Integer.valueOf(1))    = Boolean.TRUE
+     *   BooleanUtils.toBoolean(Integer.valueOf(null)) = null
+     * 
+ * + * @param value the Integer to convert + * @return Boolean.TRUE if non-zero, Boolean.FALSE if zero, + * {@code null} if {@code null} input + */ + public static Boolean toBooleanObject(Integer value) { + if (value == null) { + return null; + } + return value.intValue() == 0 ? Boolean.FALSE : Boolean.TRUE; + } + + /** + *

Converts an int to a boolean specifying the conversion values.

+ * + *
+     *   BooleanUtils.toBoolean(0, 1, 0) = false
+     *   BooleanUtils.toBoolean(1, 1, 0) = true
+     *   BooleanUtils.toBoolean(2, 1, 2) = false
+     *   BooleanUtils.toBoolean(2, 2, 0) = true
+     * 
+ * + * @param value the Integer to convert + * @param trueValue the value to match for {@code true} + * @param falseValue the value to match for {@code false} + * @return {@code true} or {@code false} + * @throws IllegalArgumentException if no match + */ + public static boolean toBoolean(int value, int trueValue, int falseValue) { + if (value == trueValue) { + return true; + } + if (value == falseValue) { + return false; + } + // no match + throw new IllegalArgumentException("The Integer did not match either specified value"); + } + + /** + *

Converts an Integer to a boolean specifying the conversion values.

+ * + *
+     *   BooleanUtils.toBoolean(Integer.valueOf(0), Integer.valueOf(1), Integer.valueOf(0)) = false
+     *   BooleanUtils.toBoolean(Integer.valueOf(1), Integer.valueOf(1), Integer.valueOf(0)) = true
+     *   BooleanUtils.toBoolean(Integer.valueOf(2), Integer.valueOf(1), Integer.valueOf(2)) = false
+     *   BooleanUtils.toBoolean(Integer.valueOf(2), Integer.valueOf(2), Integer.valueOf(0)) = true
+     *   BooleanUtils.toBoolean(null, null, Integer.valueOf(0))                     = true
+     * 
+ * + * @param value the Integer to convert + * @param trueValue the value to match for {@code true}, may be {@code null} + * @param falseValue the value to match for {@code false}, may be {@code null} + * @return {@code true} or {@code false} + * @throws IllegalArgumentException if no match + */ + public static boolean toBoolean(Integer value, Integer trueValue, Integer falseValue) { + if (value == null) { + if (trueValue == null) { + return true; + } + if (falseValue == null) { + return false; + } + } else if (value.equals(trueValue)) { + return true; + } else if (value.equals(falseValue)) { + return false; + } + // no match + throw new IllegalArgumentException("The Integer did not match either specified value"); + } + + /** + *

Converts an int to a Boolean specifying the conversion values.

+ * + *

NOTE: This returns null and will throw a NullPointerException if autoboxed to a boolean.

+ * + *
+     *   BooleanUtils.toBooleanObject(0, 0, 2, 3) = Boolean.TRUE
+     *   BooleanUtils.toBooleanObject(2, 1, 2, 3) = Boolean.FALSE
+     *   BooleanUtils.toBooleanObject(3, 1, 2, 3) = null
+     * 
+ * + * @param value the Integer to convert + * @param trueValue the value to match for {@code true} + * @param falseValue the value to match for {@code false} + * @param nullValue the value to to match for {@code null} + * @return Boolean.TRUE, Boolean.FALSE, or {@code null} + * @throws IllegalArgumentException if no match + */ + public static Boolean toBooleanObject(int value, int trueValue, int falseValue, int nullValue) { + if (value == trueValue) { + return Boolean.TRUE; + } + if (value == falseValue) { + return Boolean.FALSE; + } + if (value == nullValue) { + return null; + } + // no match + throw new IllegalArgumentException("The Integer did not match any specified value"); + } + + /** + *

Converts an Integer to a Boolean specifying the conversion values.

+ * + *

NOTE: This returns null and will throw a NullPointerException if autoboxed to a boolean.

+ * + *
+     *   BooleanUtils.toBooleanObject(Integer.valueOf(0), Integer.valueOf(0), Integer.valueOf(2), Integer.valueOf(3)) = Boolean.TRUE
+     *   BooleanUtils.toBooleanObject(Integer.valueOf(2), Integer.valueOf(1), Integer.valueOf(2), Integer.valueOf(3)) = Boolean.FALSE
+     *   BooleanUtils.toBooleanObject(Integer.valueOf(3), Integer.valueOf(1), Integer.valueOf(2), Integer.valueOf(3)) = null
+     * 
+ * + * @param value the Integer to convert + * @param trueValue the value to match for {@code true}, may be {@code null} + * @param falseValue the value to match for {@code false}, may be {@code null} + * @param nullValue the value to to match for {@code null}, may be {@code null} + * @return Boolean.TRUE, Boolean.FALSE, or {@code null} + * @throws IllegalArgumentException if no match + */ + public static Boolean toBooleanObject(Integer value, Integer trueValue, Integer falseValue, Integer nullValue) { + if (value == null) { + if (trueValue == null) { + return Boolean.TRUE; + } + if (falseValue == null) { + return Boolean.FALSE; + } + if (nullValue == null) { + return null; + } + } else if (value.equals(trueValue)) { + return Boolean.TRUE; + } else if (value.equals(falseValue)) { + return Boolean.FALSE; + } else if (value.equals(nullValue)) { + return null; + } + // no match + throw new IllegalArgumentException("The Integer did not match any specified value"); + } + + // Boolean to Integer methods + //----------------------------------------------------------------------- + /** + *

Converts a boolean to an int using the convention that + * {@code zero} is {@code false}.

+ * + *
+     *   BooleanUtils.toInteger(true)  = 1
+     *   BooleanUtils.toInteger(false) = 0
+     * 
+ * + * @param bool the boolean to convert + * @return one if {@code true}, zero if {@code false} + */ + public static int toInteger(boolean bool) { + return bool ? 1 : 0; + } + + /** + *

Converts a boolean to an Integer using the convention that + * {@code zero} is {@code false}.

+ * + *
+     *   BooleanUtils.toIntegerObject(true)  = Integer.valueOf(1)
+     *   BooleanUtils.toIntegerObject(false) = Integer.valueOf(0)
+     * 
+ * + * @param bool the boolean to convert + * @return one if {@code true}, zero if {@code false} + */ + public static Integer toIntegerObject(boolean bool) { + return bool ? NumberUtils.INTEGER_ONE : NumberUtils.INTEGER_ZERO; + } + + /** + *

Converts a Boolean to a Integer using the convention that + * {@code zero} is {@code false}.

+ * + *

{@code null} will be converted to {@code null}.

+ * + *
+     *   BooleanUtils.toIntegerObject(Boolean.TRUE)  = Integer.valueOf(1)
+     *   BooleanUtils.toIntegerObject(Boolean.FALSE) = Integer.valueOf(0)
+     * 
+ * + * @param bool the Boolean to convert + * @return one if Boolean.TRUE, zero if Boolean.FALSE, {@code null} if {@code null} + */ + public static Integer toIntegerObject(Boolean bool) { + if (bool == null) { + return null; + } + return bool.booleanValue() ? NumberUtils.INTEGER_ONE : NumberUtils.INTEGER_ZERO; + } + + /** + *

Converts a boolean to an int specifying the conversion values.

+ * + *
+     *   BooleanUtils.toInteger(true, 1, 0)  = 1
+     *   BooleanUtils.toInteger(false, 1, 0) = 0
+     * 
+ * + * @param bool the to convert + * @param trueValue the value to return if {@code true} + * @param falseValue the value to return if {@code false} + * @return the appropriate value + */ + public static int toInteger(boolean bool, int trueValue, int falseValue) { + return bool ? trueValue : falseValue; + } + + /** + *

Converts a Boolean to an int specifying the conversion values.

+ * + *
+     *   BooleanUtils.toInteger(Boolean.TRUE, 1, 0, 2)  = 1
+     *   BooleanUtils.toInteger(Boolean.FALSE, 1, 0, 2) = 0
+     *   BooleanUtils.toInteger(null, 1, 0, 2)          = 2
+     * 
+ * + * @param bool the Boolean to convert + * @param trueValue the value to return if {@code true} + * @param falseValue the value to return if {@code false} + * @param nullValue the value to return if {@code null} + * @return the appropriate value + */ + public static int toInteger(Boolean bool, int trueValue, int falseValue, int nullValue) { + if (bool == null) { + return nullValue; + } + return bool.booleanValue() ? trueValue : falseValue; + } + + /** + *

Converts a boolean to an Integer specifying the conversion values.

+ * + *
+     *   BooleanUtils.toIntegerObject(true, Integer.valueOf(1), Integer.valueOf(0))  = Integer.valueOf(1)
+     *   BooleanUtils.toIntegerObject(false, Integer.valueOf(1), Integer.valueOf(0)) = Integer.valueOf(0)
+     * 
+ * + * @param bool the to convert + * @param trueValue the value to return if {@code true}, may be {@code null} + * @param falseValue the value to return if {@code false}, may be {@code null} + * @return the appropriate value + */ + public static Integer toIntegerObject(boolean bool, Integer trueValue, Integer falseValue) { + return bool ? trueValue : falseValue; + } + + /** + *

Converts a Boolean to an Integer specifying the conversion values.

+ * + *
+     *   BooleanUtils.toIntegerObject(Boolean.TRUE, Integer.valueOf(1), Integer.valueOf(0), Integer.valueOf(2))  = Integer.valueOf(1)
+     *   BooleanUtils.toIntegerObject(Boolean.FALSE, Integer.valueOf(1), Integer.valueOf(0), Integer.valueOf(2)) = Integer.valueOf(0)
+     *   BooleanUtils.toIntegerObject(null, Integer.valueOf(1), Integer.valueOf(0), Integer.valueOf(2))          = Integer.valueOf(2)
+     * 
+ * + * @param bool the Boolean to convert + * @param trueValue the value to return if {@code true}, may be {@code null} + * @param falseValue the value to return if {@code false}, may be {@code null} + * @param nullValue the value to return if {@code null}, may be {@code null} + * @return the appropriate value + */ + public static Integer toIntegerObject(Boolean bool, Integer trueValue, Integer falseValue, Integer nullValue) { + if (bool == null) { + return nullValue; + } + return bool.booleanValue() ? trueValue : falseValue; + } + + // String to Boolean methods + //----------------------------------------------------------------------- + /** + *

Converts a String to a Boolean.

+ * + *

{@code 'true'}, {@code 'on'} or {@code 'yes'} + * (case insensitive) will return {@code true}. + * {@code 'false'}, {@code 'off'} or {@code 'no'} + * (case insensitive) will return {@code false}. + * Otherwise, {@code null} is returned.

+ * + *

NOTE: This returns null and will throw a NullPointerException if autoboxed to a boolean.

+ * + *
+     *   BooleanUtils.toBooleanObject(null)    = null
+     *   BooleanUtils.toBooleanObject("true")  = Boolean.TRUE
+     *   BooleanUtils.toBooleanObject("false") = Boolean.FALSE
+     *   BooleanUtils.toBooleanObject("on")    = Boolean.TRUE
+     *   BooleanUtils.toBooleanObject("ON")    = Boolean.TRUE
+     *   BooleanUtils.toBooleanObject("off")   = Boolean.FALSE
+     *   BooleanUtils.toBooleanObject("oFf")   = Boolean.FALSE
+     *   BooleanUtils.toBooleanObject("blue")  = null
+     * 
+ * + * @param str the String to check + * @return the Boolean value of the string, {@code null} if no match or {@code null} input + */ + public static Boolean toBooleanObject(String str) { + // Previously used equalsIgnoreCase, which was fast for interned 'true'. + // Non interned 'true' matched 15 times slower. + // + // Optimisation provides same performance as before for interned 'true'. + // Similar performance for null, 'false', and other strings not length 2/3/4. + // 'true'/'TRUE' match 4 times slower, 'tRUE'/'True' 7 times slower. + if (str == "true") { + return Boolean.TRUE; + } + if (str == null) { + return null; + } + switch (str.length()) { + case 1: { + char ch0 = str.charAt(0); + if (ch0 == 'y' || ch0 == 'Y' || + ch0 == 't' || ch0 == 'T') { + return Boolean.TRUE; + } + if (ch0 == 'n' || ch0 == 'N' || + ch0 == 'f' || ch0 == 'F') { + return Boolean.FALSE; + } + break; + } + case 2: { + char ch0 = str.charAt(0); + char ch1 = str.charAt(1); + if ((ch0 == 'o' || ch0 == 'O') && + (ch1 == 'n' || ch1 == 'N') ) { + return Boolean.TRUE; + } + if ((ch0 == 'n' || ch0 == 'N') && + (ch1 == 'o' || ch1 == 'O') ) { + return Boolean.FALSE; + } + break; + } + case 3: { + char ch0 = str.charAt(0); + char ch1 = str.charAt(1); + char ch2 = str.charAt(2); + if ((ch0 == 'y' || ch0 == 'Y') && + (ch1 == 'e' || ch1 == 'E') && + (ch2 == 's' || ch2 == 'S') ) { + return Boolean.TRUE; + } + if ((ch0 == 'o' || ch0 == 'O') && + (ch1 == 'f' || ch1 == 'F') && + (ch2 == 'f' || ch2 == 'F') ) { + return Boolean.FALSE; + } + break; + } + case 4: { + char ch0 = str.charAt(0); + char ch1 = str.charAt(1); + char ch2 = str.charAt(2); + char ch3 = str.charAt(3); + if ((ch0 == 't' || ch0 == 'T') && + (ch1 == 'r' || ch1 == 'R') && + (ch2 == 'u' || ch2 == 'U') && + (ch3 == 'e' || ch3 == 'E') ) { + return Boolean.TRUE; + } + break; + } + case 5: { + char ch0 = str.charAt(0); + char ch1 = str.charAt(1); + char ch2 = str.charAt(2); + char ch3 = str.charAt(3); + char ch4 = str.charAt(4); + if ((ch0 == 'f' || ch0 == 'F') && + (ch1 == 'a' || ch1 == 'A') && + (ch2 == 'l' || ch2 == 'L') && + (ch3 == 's' || ch3 == 'S') && + (ch4 == 'e' || ch4 == 'E') ) { + return Boolean.FALSE; + } + break; + } + } + + return null; + } + + /** + *

Converts a String to a Boolean throwing an exception if no match.

+ * + *

NOTE: This returns null and will throw a NullPointerException if autoboxed to a boolean.

+ * + *
+     *   BooleanUtils.toBooleanObject("true", "true", "false", "null")  = Boolean.TRUE
+     *   BooleanUtils.toBooleanObject("false", "true", "false", "null") = Boolean.FALSE
+     *   BooleanUtils.toBooleanObject("null", "true", "false", "null")  = null
+     * 
+ * + * @param str the String to check + * @param trueString the String to match for {@code true} (case sensitive), may be {@code null} + * @param falseString the String to match for {@code false} (case sensitive), may be {@code null} + * @param nullString the String to match for {@code null} (case sensitive), may be {@code null} + * @return the Boolean value of the string, {@code null} if either the String matches {@code nullString} + * or if {@code null} input and {@code nullString} is {@code null} + * @throws IllegalArgumentException if the String doesn't match + */ + public static Boolean toBooleanObject(String str, String trueString, String falseString, String nullString) { + if (str == null) { + if (trueString == null) { + return Boolean.TRUE; + } + if (falseString == null) { + return Boolean.FALSE; + } + if (nullString == null) { + return null; + } + } else if (str.equals(trueString)) { + return Boolean.TRUE; + } else if (str.equals(falseString)) { + return Boolean.FALSE; + } else if (str.equals(nullString)) { + return null; + } + // no match + throw new IllegalArgumentException("The String did not match any specified value"); + } + + // String to boolean methods + //----------------------------------------------------------------------- + /** + *

Converts a String to a boolean (optimised for performance).

+ * + *

{@code 'true'}, {@code 'on'} or {@code 'yes'} + * (case insensitive) will return {@code true}. Otherwise, + * {@code false} is returned.

+ * + *

This method performs 4 times faster (JDK1.4) than + * {@code Boolean.valueOf(String)}. However, this method accepts + * 'on' and 'yes' as true values. + * + *

+     *   BooleanUtils.toBoolean(null)    = false
+     *   BooleanUtils.toBoolean("true")  = true
+     *   BooleanUtils.toBoolean("TRUE")  = true
+     *   BooleanUtils.toBoolean("tRUe")  = true
+     *   BooleanUtils.toBoolean("on")    = true
+     *   BooleanUtils.toBoolean("yes")   = true
+     *   BooleanUtils.toBoolean("false") = false
+     *   BooleanUtils.toBoolean("x gti") = false
+     * 
+ * + * @param str the String to check + * @return the boolean value of the string, {@code false} if no match or the String is null + */ + public static boolean toBoolean(String str) { + return toBooleanObject(str) == Boolean.TRUE; + } + + /** + *

Converts a String to a Boolean throwing an exception if no match found.

+ * + *
+     *   BooleanUtils.toBoolean("true", "true", "false")  = true
+     *   BooleanUtils.toBoolean("false", "true", "false") = false
+     * 
+ * + * @param str the String to check + * @param trueString the String to match for {@code true} (case sensitive), may be {@code null} + * @param falseString the String to match for {@code false} (case sensitive), may be {@code null} + * @return the boolean value of the string + * @throws IllegalArgumentException if the String doesn't match + */ + public static boolean toBoolean(String str, String trueString, String falseString) { + if (str == trueString) { + return true; + } else if (str == falseString) { + return false; + } else if (str != null) { + if (str.equals(trueString)) { + return true; + } else if (str.equals(falseString)) { + return false; + } + } + // no match + throw new IllegalArgumentException("The String did not match either specified value"); + } + + // Boolean to String methods + //----------------------------------------------------------------------- + /** + *

Converts a Boolean to a String returning {@code 'true'}, + * {@code 'false'}, or {@code null}.

+ * + *
+     *   BooleanUtils.toStringTrueFalse(Boolean.TRUE)  = "true"
+     *   BooleanUtils.toStringTrueFalse(Boolean.FALSE) = "false"
+     *   BooleanUtils.toStringTrueFalse(null)          = null;
+     * 
+ * + * @param bool the Boolean to check + * @return {@code 'true'}, {@code 'false'}, or {@code null} + */ + public static String toStringTrueFalse(Boolean bool) { + return toString(bool, "true", "false", null); + } + + /** + *

Converts a Boolean to a String returning {@code 'on'}, + * {@code 'off'}, or {@code null}.

+ * + *
+     *   BooleanUtils.toStringOnOff(Boolean.TRUE)  = "on"
+     *   BooleanUtils.toStringOnOff(Boolean.FALSE) = "off"
+     *   BooleanUtils.toStringOnOff(null)          = null;
+     * 
+ * + * @param bool the Boolean to check + * @return {@code 'on'}, {@code 'off'}, or {@code null} + */ + public static String toStringOnOff(Boolean bool) { + return toString(bool, "on", "off", null); + } + + /** + *

Converts a Boolean to a String returning {@code 'yes'}, + * {@code 'no'}, or {@code null}.

+ * + *
+     *   BooleanUtils.toStringYesNo(Boolean.TRUE)  = "yes"
+     *   BooleanUtils.toStringYesNo(Boolean.FALSE) = "no"
+     *   BooleanUtils.toStringYesNo(null)          = null;
+     * 
+ * + * @param bool the Boolean to check + * @return {@code 'yes'}, {@code 'no'}, or {@code null} + */ + public static String toStringYesNo(Boolean bool) { + return toString(bool, "yes", "no", null); + } + + /** + *

Converts a Boolean to a String returning one of the input Strings.

+ * + *
+     *   BooleanUtils.toString(Boolean.TRUE, "true", "false", null)   = "true"
+     *   BooleanUtils.toString(Boolean.FALSE, "true", "false", null)  = "false"
+     *   BooleanUtils.toString(null, "true", "false", null)           = null;
+     * 
+ * + * @param bool the Boolean to check + * @param trueString the String to return if {@code true}, may be {@code null} + * @param falseString the String to return if {@code false}, may be {@code null} + * @param nullString the String to return if {@code null}, may be {@code null} + * @return one of the three input Strings + */ + public static String toString(Boolean bool, String trueString, String falseString, String nullString) { + if (bool == null) { + return nullString; + } + return bool.booleanValue() ? trueString : falseString; + } + + // boolean to String methods + //----------------------------------------------------------------------- + /** + *

Converts a boolean to a String returning {@code 'true'} + * or {@code 'false'}.

+ * + *
+     *   BooleanUtils.toStringTrueFalse(true)   = "true"
+     *   BooleanUtils.toStringTrueFalse(false)  = "false"
+     * 
+ * + * @param bool the Boolean to check + * @return {@code 'true'}, {@code 'false'}, or {@code null} + */ + public static String toStringTrueFalse(boolean bool) { + return toString(bool, "true", "false"); + } + + /** + *

Converts a boolean to a String returning {@code 'on'} + * or {@code 'off'}.

+ * + *
+     *   BooleanUtils.toStringOnOff(true)   = "on"
+     *   BooleanUtils.toStringOnOff(false)  = "off"
+     * 
+ * + * @param bool the Boolean to check + * @return {@code 'on'}, {@code 'off'}, or {@code null} + */ + public static String toStringOnOff(boolean bool) { + return toString(bool, "on", "off"); + } + + /** + *

Converts a boolean to a String returning {@code 'yes'} + * or {@code 'no'}.

+ * + *
+     *   BooleanUtils.toStringYesNo(true)   = "yes"
+     *   BooleanUtils.toStringYesNo(false)  = "no"
+     * 
+ * + * @param bool the Boolean to check + * @return {@code 'yes'}, {@code 'no'}, or {@code null} + */ + public static String toStringYesNo(boolean bool) { + return toString(bool, "yes", "no"); + } + + /** + *

Converts a boolean to a String returning one of the input Strings.

+ * + *
+     *   BooleanUtils.toString(true, "true", "false")   = "true"
+     *   BooleanUtils.toString(false, "true", "false")  = "false"
+     * 
+ * + * @param bool the Boolean to check + * @param trueString the String to return if {@code true}, may be {@code null} + * @param falseString the String to return if {@code false}, may be {@code null} + * @return one of the two input Strings + */ + public static String toString(boolean bool, String trueString, String falseString) { + return bool ? trueString : falseString; + } + + // logical operations + // ---------------------------------------------------------------------- + /** + *

Performs an and on a set of booleans.

+ * + *
+     *   BooleanUtils.and(true, true)         = true
+     *   BooleanUtils.and(false, false)       = false
+     *   BooleanUtils.and(true, false)        = false
+     *   BooleanUtils.and(true, true, false)  = false
+     *   BooleanUtils.and(true, true, true)   = true
+     * 
+ * + * @param array an array of {@code boolean}s + * @return {@code true} if the and is successful. + * @throws IllegalArgumentException if {@code array} is {@code null} + * @throws IllegalArgumentException if {@code array} is empty. + * @since 3.0.1 + */ + public static boolean and(boolean... array) { + // Validates input + if (array == null) { + throw new IllegalArgumentException("The Array must not be null"); + } + if (array.length == 0) { + throw new IllegalArgumentException("Array is empty"); + } + for (boolean element : array) { + if (!element) { + return false; + } + } + return true; + } + + /** + *

Performs an and on an array of Booleans.

+ * + *
+     *   BooleanUtils.and(Boolean.TRUE, Boolean.TRUE)                 = Boolean.TRUE
+     *   BooleanUtils.and(Boolean.FALSE, Boolean.FALSE)               = Boolean.FALSE
+     *   BooleanUtils.and(Boolean.TRUE, Boolean.FALSE)                = Boolean.FALSE
+     *   BooleanUtils.and(Boolean.TRUE, Boolean.TRUE, Boolean.TRUE)   = Boolean.TRUE
+     *   BooleanUtils.and(Boolean.FALSE, Boolean.FALSE, Boolean.TRUE) = Boolean.FALSE
+     *   BooleanUtils.and(Boolean.TRUE, Boolean.FALSE, Boolean.TRUE)  = Boolean.FALSE
+     * 
+ * + * @param array an array of {@code Boolean}s + * @return {@code true} if the and is successful. + * @throws IllegalArgumentException if {@code array} is {@code null} + * @throws IllegalArgumentException if {@code array} is empty. + * @throws IllegalArgumentException if {@code array} contains a {@code null} + * @since 3.0.1 + */ + public static Boolean and(Boolean... array) { + if (array == null) { + throw new IllegalArgumentException("The Array must not be null"); + } + if (array.length == 0) { + throw new IllegalArgumentException("Array is empty"); + } + try { + boolean[] primitive = ArrayUtils.toPrimitive(array); + return and(primitive) ? Boolean.TRUE : Boolean.FALSE; + } catch (NullPointerException ex) { + throw new IllegalArgumentException("The array must not contain any null elements"); + } + } + + /** + *

Performs an or on a set of booleans.

+ * + *
+     *   BooleanUtils.or(true, true)          = true
+     *   BooleanUtils.or(false, false)        = false
+     *   BooleanUtils.or(true, false)         = true
+     *   BooleanUtils.or(true, true, false)   = true
+     *   BooleanUtils.or(true, true, true)    = true
+     *   BooleanUtils.or(false, false, false) = false
+     * 
+ * + * @param array an array of {@code boolean}s + * @return {@code true} if the or is successful. + * @throws IllegalArgumentException if {@code array} is {@code null} + * @throws IllegalArgumentException if {@code array} is empty. + * @since 3.0.1 + */ + public static boolean or(boolean... array) { + // Validates input + if (array == null) { + throw new IllegalArgumentException("The Array must not be null"); + } + if (array.length == 0) { + throw new IllegalArgumentException("Array is empty"); + } + for (boolean element : array) { + if (element) { + return true; + } + } + return false; + } + + /** + *

Performs an or on an array of Booleans.

+ * + *
+     *   BooleanUtils.or(Boolean.TRUE, Boolean.TRUE)                  = Boolean.TRUE
+     *   BooleanUtils.or(Boolean.FALSE, Boolean.FALSE)                = Boolean.FALSE
+     *   BooleanUtils.or(Boolean.TRUE, Boolean.FALSE)                 = Boolean.TRUE
+     *   BooleanUtils.or(Boolean.TRUE, Boolean.TRUE, Boolean.TRUE)    = Boolean.TRUE
+     *   BooleanUtils.or(Boolean.FALSE, Boolean.FALSE, Boolean.TRUE)  = Boolean.TRUE
+     *   BooleanUtils.or(Boolean.TRUE, Boolean.FALSE, Boolean.TRUE)   = Boolean.TRUE
+     *   BooleanUtils.or(Boolean.FALSE, Boolean.FALSE, Boolean.FALSE) = Boolean.FALSE
+     * 
+ * + * @param array an array of {@code Boolean}s + * @return {@code true} if the or is successful. + * @throws IllegalArgumentException if {@code array} is {@code null} + * @throws IllegalArgumentException if {@code array} is empty. + * @throws IllegalArgumentException if {@code array} contains a {@code null} + * @since 3.0.1 + */ + public static Boolean or(Boolean... array) { + if (array == null) { + throw new IllegalArgumentException("The Array must not be null"); + } + if (array.length == 0) { + throw new IllegalArgumentException("Array is empty"); + } + try { + boolean[] primitive = ArrayUtils.toPrimitive(array); + return or(primitive) ? Boolean.TRUE : Boolean.FALSE; + } catch (NullPointerException ex) { + throw new IllegalArgumentException("The array must not contain any null elements"); + } + } + + /** + *

Performs an xor on a set of booleans.

+ * + *
+     *   BooleanUtils.xor(true, true)   = false
+     *   BooleanUtils.xor(false, false) = false
+     *   BooleanUtils.xor(true, false)  = true
+     *   BooleanUtils.xor(true, true)   = false
+     *   BooleanUtils.xor(false, false) = false
+     *   BooleanUtils.xor(true, false)  = true
+     * 
+ * + * @param array an array of {@code boolean}s + * @return {@code true} if the xor is successful. + * @throws IllegalArgumentException if {@code array} is {@code null} + * @throws IllegalArgumentException if {@code array} is empty. + */ + public static boolean xor(boolean... array) { + // Validates input + if (array == null) { + throw new IllegalArgumentException("The Array must not be null"); + } + if (array.length == 0) { + throw new IllegalArgumentException("Array is empty"); + } + + // Loops through array, comparing each item + int trueCount = 0; + for (boolean element : array) { + // If item is true, and trueCount is < 1, increments count + // Else, xor fails + if (element) { + if (trueCount < 1) { + trueCount++; + } else { + return false; + } + } + } + + // Returns true if there was exactly 1 true item + return trueCount == 1; + } + + /** + *

Performs an xor on an array of Booleans.

+ * + *
+     *   BooleanUtils.xor(new Boolean[] { Boolean.TRUE, Boolean.TRUE })   = Boolean.FALSE
+     *   BooleanUtils.xor(new Boolean[] { Boolean.FALSE, Boolean.FALSE }) = Boolean.FALSE
+     *   BooleanUtils.xor(new Boolean[] { Boolean.TRUE, Boolean.FALSE })  = Boolean.TRUE
+     * 
+ * + * @param array an array of {@code Boolean}s + * @return {@code true} if the xor is successful. + * @throws IllegalArgumentException if {@code array} is {@code null} + * @throws IllegalArgumentException if {@code array} is empty. + * @throws IllegalArgumentException if {@code array} contains a {@code null} + */ + public static Boolean xor(Boolean... array) { + if (array == null) { + throw new IllegalArgumentException("The Array must not be null"); + } + if (array.length == 0) { + throw new IllegalArgumentException("Array is empty"); + } + try { + boolean[] primitive = ArrayUtils.toPrimitive(array); + return xor(primitive) ? Boolean.TRUE : Boolean.FALSE; + } catch (NullPointerException ex) { + throw new IllegalArgumentException("The array must not contain any null elements"); + } + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/BooleanUtilsTest.java b/ApacheCommonsLang/org/apache/commons/lang3/BooleanUtilsTest.java new file mode 100644 index 0000000..2d4079c --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/BooleanUtilsTest.java @@ -0,0 +1,984 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Modifier; + +import org.junit.Test; + +/** + * Unit tests {@link org.apache.commons.lang3.BooleanUtils}. + * + * @version $Id: BooleanUtilsTest.java 1309910 2012-04-05 15:52:43Z ggregory $ + */ +public class BooleanUtilsTest { + + //----------------------------------------------------------------------- + @Test + public void testConstructor() { + assertNotNull(new BooleanUtils()); + Constructor[] cons = BooleanUtils.class.getDeclaredConstructors(); + assertEquals(1, cons.length); + assertTrue(Modifier.isPublic(cons[0].getModifiers())); + assertTrue(Modifier.isPublic(BooleanUtils.class.getModifiers())); + assertFalse(Modifier.isFinal(BooleanUtils.class.getModifiers())); + } + + //----------------------------------------------------------------------- + @Test + public void test_negate_Boolean() { + assertSame(null, BooleanUtils.negate(null)); + assertSame(Boolean.TRUE, BooleanUtils.negate(Boolean.FALSE)); + assertSame(Boolean.FALSE, BooleanUtils.negate(Boolean.TRUE)); + } + + //----------------------------------------------------------------------- + @Test + public void test_isTrue_Boolean() { + assertTrue(BooleanUtils.isTrue(Boolean.TRUE)); + assertFalse(BooleanUtils.isTrue(Boolean.FALSE)); + assertFalse(BooleanUtils.isTrue((Boolean) null)); + } + + @Test + public void test_isNotTrue_Boolean() { + assertFalse(BooleanUtils.isNotTrue(Boolean.TRUE)); + assertTrue(BooleanUtils.isNotTrue(Boolean.FALSE)); + assertTrue(BooleanUtils.isNotTrue((Boolean) null)); + } + + //----------------------------------------------------------------------- + @Test + public void test_isFalse_Boolean() { + assertFalse(BooleanUtils.isFalse(Boolean.TRUE)); + assertTrue(BooleanUtils.isFalse(Boolean.FALSE)); + assertFalse(BooleanUtils.isFalse((Boolean) null)); + } + + @Test + public void test_isNotFalse_Boolean() { + assertTrue(BooleanUtils.isNotFalse(Boolean.TRUE)); + assertFalse(BooleanUtils.isNotFalse(Boolean.FALSE)); + assertTrue(BooleanUtils.isNotFalse((Boolean) null)); + } + + //----------------------------------------------------------------------- + @Test + public void test_toBoolean_Boolean() { + assertTrue(BooleanUtils.toBoolean(Boolean.TRUE)); + assertFalse(BooleanUtils.toBoolean(Boolean.FALSE)); + assertFalse(BooleanUtils.toBoolean((Boolean) null)); + } + + @Test + public void test_toBooleanDefaultIfNull_Boolean_boolean() { + assertTrue(BooleanUtils.toBooleanDefaultIfNull(Boolean.TRUE, true)); + assertTrue(BooleanUtils.toBooleanDefaultIfNull(Boolean.TRUE, false)); + assertFalse(BooleanUtils.toBooleanDefaultIfNull(Boolean.FALSE, true)); + assertFalse(BooleanUtils.toBooleanDefaultIfNull(Boolean.FALSE, false)); + assertTrue(BooleanUtils.toBooleanDefaultIfNull((Boolean) null, true)); + assertFalse(BooleanUtils.toBooleanDefaultIfNull((Boolean) null, false)); + } + + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + @Test + public void test_toBoolean_int() { + assertTrue(BooleanUtils.toBoolean(1)); + assertTrue(BooleanUtils.toBoolean(-1)); + assertFalse(BooleanUtils.toBoolean(0)); + } + + @Test + public void test_toBooleanObject_int() { + assertEquals(Boolean.TRUE, BooleanUtils.toBooleanObject(1)); + assertEquals(Boolean.TRUE, BooleanUtils.toBooleanObject(-1)); + assertEquals(Boolean.FALSE, BooleanUtils.toBooleanObject(0)); + } + + @Test + public void test_toBooleanObject_Integer() { + assertEquals(Boolean.TRUE, BooleanUtils.toBooleanObject(Integer.valueOf(1))); + assertEquals(Boolean.TRUE, BooleanUtils.toBooleanObject(Integer.valueOf(-1))); + assertEquals(Boolean.FALSE, BooleanUtils.toBooleanObject(Integer.valueOf(0))); + assertEquals(null, BooleanUtils.toBooleanObject((Integer) null)); + } + + //----------------------------------------------------------------------- + @Test + public void test_toBoolean_int_int_int() { + assertTrue(BooleanUtils.toBoolean(6, 6, 7)); + assertFalse(BooleanUtils.toBoolean(7, 6, 7)); + } + + @Test(expected = IllegalArgumentException.class) + public void test_toBoolean_int_int_int_noMatch() { + BooleanUtils.toBoolean(8, 6, 7); + } + + @Test + public void test_toBoolean_Integer_Integer_Integer() { + Integer six = Integer.valueOf(6); + Integer seven = Integer.valueOf(7); + + assertTrue(BooleanUtils.toBoolean((Integer) null, null, seven)); + assertFalse(BooleanUtils.toBoolean((Integer) null, six, null)); + + assertTrue(BooleanUtils.toBoolean(Integer.valueOf(6), six, seven)); + assertFalse(BooleanUtils.toBoolean(Integer.valueOf(7), six, seven)); + } + + @Test(expected = IllegalArgumentException.class) + public void test_toBoolean_Integer_Integer_Integer_nullValue() { + BooleanUtils.toBoolean(null, Integer.valueOf(6), Integer.valueOf(7)); + } + + @Test(expected = IllegalArgumentException.class) + public void test_toBoolean_Integer_Integer_Integer_noMatch() { + BooleanUtils.toBoolean(Integer.valueOf(8), Integer.valueOf(6), Integer.valueOf(7)); + } + + //----------------------------------------------------------------------- + @Test + public void test_toBooleanObject_int_int_int() { + assertEquals(Boolean.TRUE, BooleanUtils.toBooleanObject(6, 6, 7, 8)); + assertEquals(Boolean.FALSE, BooleanUtils.toBooleanObject(7, 6, 7, 8)); + assertEquals(null, BooleanUtils.toBooleanObject(8, 6, 7, 8)); + } + + @Test(expected = IllegalArgumentException.class) + public void test_toBooleanObject_int_int_int_noMatch() { + BooleanUtils.toBooleanObject(9, 6, 7, 8); + } + + @Test + public void test_toBooleanObject_Integer_Integer_Integer_Integer() { + Integer six = Integer.valueOf(6); + Integer seven = Integer.valueOf(7); + Integer eight = Integer.valueOf(8); + + assertSame(Boolean.TRUE, BooleanUtils.toBooleanObject((Integer) null, null, seven, eight)); + assertSame(Boolean.FALSE, BooleanUtils.toBooleanObject((Integer) null, six, null, eight)); + assertSame(null, BooleanUtils.toBooleanObject((Integer) null, six, seven, null)); + + assertEquals(Boolean.TRUE, BooleanUtils.toBooleanObject(Integer.valueOf(6), six, seven, eight)); + assertEquals(Boolean.FALSE, BooleanUtils.toBooleanObject(Integer.valueOf(7), six, seven, eight)); + assertEquals(null, BooleanUtils.toBooleanObject(Integer.valueOf(8), six, seven, eight)); + } + + @Test(expected = IllegalArgumentException.class) + public void test_toBooleanObject_Integer_Integer_Integer_Integer_nullValue() { + BooleanUtils.toBooleanObject(null, Integer.valueOf(6), Integer.valueOf(7), Integer.valueOf(8)); + } + + @Test(expected = IllegalArgumentException.class) + public void test_toBooleanObject_Integer_Integer_Integer_Integer_noMatch() { + BooleanUtils.toBooleanObject(Integer.valueOf(9), Integer.valueOf(6), Integer.valueOf(7), Integer.valueOf(8)); + } + + //----------------------------------------------------------------------- + @Test + public void test_toInteger_boolean() { + assertEquals(1, BooleanUtils.toInteger(true)); + assertEquals(0, BooleanUtils.toInteger(false)); + } + + @Test + public void test_toIntegerObject_boolean() { + assertEquals(Integer.valueOf(1), BooleanUtils.toIntegerObject(true)); + assertEquals(Integer.valueOf(0), BooleanUtils.toIntegerObject(false)); + } + + @Test + public void test_toIntegerObject_Boolean() { + assertEquals(Integer.valueOf(1), BooleanUtils.toIntegerObject(Boolean.TRUE)); + assertEquals(Integer.valueOf(0), BooleanUtils.toIntegerObject(Boolean.FALSE)); + assertEquals(null, BooleanUtils.toIntegerObject((Boolean) null)); + } + + //----------------------------------------------------------------------- + @Test + public void test_toInteger_boolean_int_int() { + assertEquals(6, BooleanUtils.toInteger(true, 6, 7)); + assertEquals(7, BooleanUtils.toInteger(false, 6, 7)); + } + + @Test + public void test_toInteger_Boolean_int_int_int() { + assertEquals(6, BooleanUtils.toInteger(Boolean.TRUE, 6, 7, 8)); + assertEquals(7, BooleanUtils.toInteger(Boolean.FALSE, 6, 7, 8)); + assertEquals(8, BooleanUtils.toInteger(null, 6, 7, 8)); + } + + @Test + public void test_toIntegerObject_boolean_Integer_Integer() { + Integer six = Integer.valueOf(6); + Integer seven = Integer.valueOf(7); + assertEquals(six, BooleanUtils.toIntegerObject(true, six, seven)); + assertEquals(seven, BooleanUtils.toIntegerObject(false, six, seven)); + } + + @Test + public void test_toIntegerObject_Boolean_Integer_Integer_Integer() { + Integer six = Integer.valueOf(6); + Integer seven = Integer.valueOf(7); + Integer eight = Integer.valueOf(8); + assertEquals(six, BooleanUtils.toIntegerObject(Boolean.TRUE, six, seven, eight)); + assertEquals(seven, BooleanUtils.toIntegerObject(Boolean.FALSE, six, seven, eight)); + assertEquals(eight, BooleanUtils.toIntegerObject((Boolean) null, six, seven, eight)); + assertEquals(null, BooleanUtils.toIntegerObject((Boolean) null, six, seven, null)); + } + + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + @Test + public void test_toBooleanObject_String() { + assertEquals(null, BooleanUtils.toBooleanObject((String) null)); + assertEquals(null, BooleanUtils.toBooleanObject("")); + assertEquals(Boolean.FALSE, BooleanUtils.toBooleanObject("false")); + assertEquals(Boolean.FALSE, BooleanUtils.toBooleanObject("no")); + assertEquals(Boolean.FALSE, BooleanUtils.toBooleanObject("off")); + assertEquals(Boolean.FALSE, BooleanUtils.toBooleanObject("FALSE")); + assertEquals(Boolean.FALSE, BooleanUtils.toBooleanObject("NO")); + assertEquals(Boolean.FALSE, BooleanUtils.toBooleanObject("OFF")); + assertEquals(null, BooleanUtils.toBooleanObject("oof")); + assertEquals(Boolean.TRUE, BooleanUtils.toBooleanObject("true")); + assertEquals(Boolean.TRUE, BooleanUtils.toBooleanObject("yes")); + assertEquals(Boolean.TRUE, BooleanUtils.toBooleanObject("on")); + assertEquals(Boolean.TRUE, BooleanUtils.toBooleanObject("TRUE")); + assertEquals(Boolean.TRUE, BooleanUtils.toBooleanObject("ON")); + assertEquals(Boolean.TRUE, BooleanUtils.toBooleanObject("YES")); + assertEquals(Boolean.TRUE, BooleanUtils.toBooleanObject("TruE")); + assertEquals(Boolean.TRUE, BooleanUtils.toBooleanObject("TruE")); + + assertEquals(Boolean.TRUE, BooleanUtils.toBooleanObject("y")); + assertEquals(Boolean.TRUE, BooleanUtils.toBooleanObject("Y")); + assertEquals(Boolean.TRUE, BooleanUtils.toBooleanObject("t")); + assertEquals(Boolean.TRUE, BooleanUtils.toBooleanObject("T")); + assertEquals(Boolean.FALSE, BooleanUtils.toBooleanObject("f")); + assertEquals(Boolean.FALSE, BooleanUtils.toBooleanObject("F")); + assertEquals(Boolean.FALSE, BooleanUtils.toBooleanObject("n")); + assertEquals(Boolean.FALSE, BooleanUtils.toBooleanObject("N")); + assertEquals(null, BooleanUtils.toBooleanObject("z")); + + assertEquals(null, BooleanUtils.toBooleanObject("ab")); + assertEquals(null, BooleanUtils.toBooleanObject("yoo")); + } + + @Test + public void test_toBooleanObject_String_String_String_String() { + assertSame(Boolean.TRUE, BooleanUtils.toBooleanObject((String) null, null, "N", "U")); + assertSame(Boolean.FALSE, BooleanUtils.toBooleanObject((String) null, "Y", null, "U")); + assertSame(null, BooleanUtils.toBooleanObject((String) null, "Y", "N", null)); + + assertEquals(Boolean.TRUE, BooleanUtils.toBooleanObject("Y", "Y", "N", "U")); + assertEquals(Boolean.FALSE, BooleanUtils.toBooleanObject("N", "Y", "N", "U")); + assertEquals(null, BooleanUtils.toBooleanObject("U", "Y", "N", "U")); + } + + @Test(expected = IllegalArgumentException.class) + public void test_toBooleanObject_String_String_String_String_nullValue() { + BooleanUtils.toBooleanObject((String) null, "Y", "N", "U"); + } + + @Test(expected = IllegalArgumentException.class) + public void test_toBooleanObject_String_String_String_String_noMatch() { + BooleanUtils.toBooleanObject("X", "Y", "N", "U"); + } + + //----------------------------------------------------------------------- + @Test + public void test_toBoolean_String() { + assertFalse(BooleanUtils.toBoolean((String) null)); + assertFalse(BooleanUtils.toBoolean("")); + assertFalse(BooleanUtils.toBoolean("off")); + assertFalse(BooleanUtils.toBoolean("oof")); + assertFalse(BooleanUtils.toBoolean("yep")); + assertFalse(BooleanUtils.toBoolean("trux")); + assertFalse(BooleanUtils.toBoolean("false")); + assertFalse(BooleanUtils.toBoolean("a")); + assertTrue(BooleanUtils.toBoolean("true")); // interned handled differently + assertTrue(BooleanUtils.toBoolean(new StringBuffer("tr").append("ue").toString())); + assertTrue(BooleanUtils.toBoolean("truE")); + assertTrue(BooleanUtils.toBoolean("trUe")); + assertTrue(BooleanUtils.toBoolean("trUE")); + assertTrue(BooleanUtils.toBoolean("tRue")); + assertTrue(BooleanUtils.toBoolean("tRuE")); + assertTrue(BooleanUtils.toBoolean("tRUe")); + assertTrue(BooleanUtils.toBoolean("tRUE")); + assertTrue(BooleanUtils.toBoolean("TRUE")); + assertTrue(BooleanUtils.toBoolean("TRUe")); + assertTrue(BooleanUtils.toBoolean("TRuE")); + assertTrue(BooleanUtils.toBoolean("TRue")); + assertTrue(BooleanUtils.toBoolean("TrUE")); + assertTrue(BooleanUtils.toBoolean("TrUe")); + assertTrue(BooleanUtils.toBoolean("TruE")); + assertTrue(BooleanUtils.toBoolean("True")); + assertTrue(BooleanUtils.toBoolean("on")); + assertTrue(BooleanUtils.toBoolean("oN")); + assertTrue(BooleanUtils.toBoolean("On")); + assertTrue(BooleanUtils.toBoolean("ON")); + assertTrue(BooleanUtils.toBoolean("yes")); + assertTrue(BooleanUtils.toBoolean("yeS")); + assertTrue(BooleanUtils.toBoolean("yEs")); + assertTrue(BooleanUtils.toBoolean("yES")); + assertTrue(BooleanUtils.toBoolean("Yes")); + assertTrue(BooleanUtils.toBoolean("YeS")); + assertTrue(BooleanUtils.toBoolean("YEs")); + assertTrue(BooleanUtils.toBoolean("YES")); + assertFalse(BooleanUtils.toBoolean("yes?")); + assertFalse(BooleanUtils.toBoolean("tru")); + + assertFalse(BooleanUtils.toBoolean("no")); + assertFalse(BooleanUtils.toBoolean("off")); + assertFalse(BooleanUtils.toBoolean("yoo")); + } + + @Test + public void test_toBoolean_String_String_String() { + assertTrue(BooleanUtils.toBoolean((String) null, null, "N")); + assertFalse(BooleanUtils.toBoolean((String) null, "Y", null)); + assertTrue(BooleanUtils.toBoolean("Y", "Y", "N")); + assertTrue(BooleanUtils.toBoolean("Y", new String("Y"), new String("N"))); + assertFalse(BooleanUtils.toBoolean("N", "Y", "N")); + assertFalse(BooleanUtils.toBoolean("N", new String("Y"), new String("N"))); + assertTrue(BooleanUtils.toBoolean((String) null, null, null)); + assertTrue(BooleanUtils.toBoolean("Y", "Y", "Y")); + assertTrue(BooleanUtils.toBoolean("Y", new String("Y"), new String("Y"))); + } + + @Test(expected = IllegalArgumentException.class) + public void test_toBoolean_String_String_String_nullValue() { + BooleanUtils.toBoolean(null, "Y", "N"); + } + + @Test(expected = IllegalArgumentException.class) + public void test_toBoolean_String_String_String_noMatch() { + BooleanUtils.toBoolean("X", "Y", "N"); + } + + //----------------------------------------------------------------------- + @Test + public void test_toStringTrueFalse_Boolean() { + assertEquals(null, BooleanUtils.toStringTrueFalse((Boolean) null)); + assertEquals("true", BooleanUtils.toStringTrueFalse(Boolean.TRUE)); + assertEquals("false", BooleanUtils.toStringTrueFalse(Boolean.FALSE)); + } + + @Test + public void test_toStringOnOff_Boolean() { + assertEquals(null, BooleanUtils.toStringOnOff((Boolean) null)); + assertEquals("on", BooleanUtils.toStringOnOff(Boolean.TRUE)); + assertEquals("off", BooleanUtils.toStringOnOff(Boolean.FALSE)); + } + + @Test + public void test_toStringYesNo_Boolean() { + assertEquals(null, BooleanUtils.toStringYesNo((Boolean) null)); + assertEquals("yes", BooleanUtils.toStringYesNo(Boolean.TRUE)); + assertEquals("no", BooleanUtils.toStringYesNo(Boolean.FALSE)); + } + + @Test + public void test_toString_Boolean_String_String_String() { + assertEquals("U", BooleanUtils.toString((Boolean) null, "Y", "N", "U")); + assertEquals("Y", BooleanUtils.toString(Boolean.TRUE, "Y", "N", "U")); + assertEquals("N", BooleanUtils.toString(Boolean.FALSE, "Y", "N", "U")); + } + + //----------------------------------------------------------------------- + @Test + public void test_toStringTrueFalse_boolean() { + assertEquals("true", BooleanUtils.toStringTrueFalse(true)); + assertEquals("false", BooleanUtils.toStringTrueFalse(false)); + } + + @Test + public void test_toStringOnOff_boolean() { + assertEquals("on", BooleanUtils.toStringOnOff(true)); + assertEquals("off", BooleanUtils.toStringOnOff(false)); + } + + @Test + public void test_toStringYesNo_boolean() { + assertEquals("yes", BooleanUtils.toStringYesNo(true)); + assertEquals("no", BooleanUtils.toStringYesNo(false)); + } + + @Test + public void test_toString_boolean_String_String_String() { + assertEquals("Y", BooleanUtils.toString(true, "Y", "N")); + assertEquals("N", BooleanUtils.toString(false, "Y", "N")); + } + + // testXor + // ----------------------------------------------------------------------- + @Test(expected = IllegalArgumentException.class) + public void testXor_primitive_nullInput() { + BooleanUtils.xor((boolean[]) null); + } + + @Test(expected = IllegalArgumentException.class) + public void testXor_primitive_emptyInput() { + BooleanUtils.xor(new boolean[] {}); + } + + @Test + public void testXor_primitive_validInput_2items() { + assertTrue( + "True result for (true, true)", + ! BooleanUtils.xor(new boolean[] { true, true })); + + assertTrue( + "True result for (false, false)", + ! BooleanUtils.xor(new boolean[] { false, false })); + + assertTrue( + "False result for (true, false)", + BooleanUtils.xor(new boolean[] { true, false })); + + assertTrue( + "False result for (false, true)", + BooleanUtils.xor(new boolean[] { false, true })); + } + + @Test + public void testXor_primitive_validInput_3items() { + assertTrue( + "False result for (false, false, true)", + BooleanUtils.xor(new boolean[] { false, false, true })); + + assertTrue( + "False result for (false, true, false)", + BooleanUtils.xor(new boolean[] { false, true, false })); + + assertTrue( + "False result for (true, false, false)", + BooleanUtils.xor(new boolean[] { true, false, false })); + + assertTrue( + "True result for (true, true, true)", + ! BooleanUtils.xor(new boolean[] { true, true, true })); + + assertTrue( + "True result for (false, false)", + ! BooleanUtils.xor(new boolean[] { false, false, false })); + + assertTrue( + "True result for (true, true, false)", + ! BooleanUtils.xor(new boolean[] { true, true, false })); + + assertTrue( + "True result for (true, false, true)", + ! BooleanUtils.xor(new boolean[] { true, false, true })); + + assertTrue( + "False result for (false, true, true)", + ! BooleanUtils.xor(new boolean[] { false, true, true })); + } + + @Test(expected = IllegalArgumentException.class) + public void testXor_object_nullInput() { + BooleanUtils.xor((Boolean[]) null); + } + + @Test(expected = IllegalArgumentException.class) + public void testXor_object_emptyInput() { + BooleanUtils.xor(new Boolean[] {}); + } + + @Test(expected = IllegalArgumentException.class) + public void testXor_object_nullElementInput() { + BooleanUtils.xor(new Boolean[] {null}); + } + + @Test + public void testXor_object_validInput_2items() { + assertTrue( + "True result for (true, true)", + ! BooleanUtils + .xor(new Boolean[] { Boolean.TRUE, Boolean.TRUE }) + .booleanValue()); + + assertTrue( + "True result for (false, false)", + ! BooleanUtils + .xor(new Boolean[] { Boolean.FALSE, Boolean.FALSE }) + .booleanValue()); + + assertTrue( + "False result for (true, false)", + BooleanUtils + .xor(new Boolean[] { Boolean.TRUE, Boolean.FALSE }) + .booleanValue()); + + assertTrue( + "False result for (false, true)", + BooleanUtils + .xor(new Boolean[] { Boolean.FALSE, Boolean.TRUE }) + .booleanValue()); + } + + @Test + public void testXor_object_validInput_3items() { + assertTrue( + "False result for (false, false, true)", + BooleanUtils + .xor( + new Boolean[] { + Boolean.FALSE, + Boolean.FALSE, + Boolean.TRUE }) + .booleanValue()); + + assertTrue( + "False result for (false, true, false)", + BooleanUtils + .xor( + new Boolean[] { + Boolean.FALSE, + Boolean.TRUE, + Boolean.FALSE }) + .booleanValue()); + + assertTrue( + "False result for (true, false, false)", + BooleanUtils + .xor( + new Boolean[] { + Boolean.TRUE, + Boolean.FALSE, + Boolean.FALSE }) + .booleanValue()); + + assertTrue( + "True result for (true, true, true)", + ! BooleanUtils + .xor(new Boolean[] { Boolean.TRUE, Boolean.TRUE, Boolean.TRUE }) + .booleanValue()); + + assertTrue( + "True result for (false, false)", + ! BooleanUtils.xor( + new Boolean[] { + Boolean.FALSE, + Boolean.FALSE, + Boolean.FALSE }) + .booleanValue()); + + assertTrue( + "True result for (true, true, false)", + ! BooleanUtils.xor( + new Boolean[] { + Boolean.TRUE, + Boolean.TRUE, + Boolean.FALSE }) + .booleanValue()); + + assertTrue( + "True result for (true, false, true)", + ! BooleanUtils.xor( + new Boolean[] { + Boolean.TRUE, + Boolean.FALSE, + Boolean.TRUE }) + .booleanValue()); + + assertTrue( + "False result for (false, true, true)", + ! BooleanUtils.xor( + new Boolean[] { + Boolean.FALSE, + Boolean.TRUE, + Boolean.TRUE }) + .booleanValue()); + } + + // testAnd + // ----------------------------------------------------------------------- + @Test(expected = IllegalArgumentException.class) + public void testAnd_primitive_nullInput() { + BooleanUtils.and((boolean[]) null); + } + + @Test(expected = IllegalArgumentException.class) + public void testAnd_primitive_emptyInput() { + BooleanUtils.and(new boolean[] {}); + } + + @Test + public void testAnd_primitive_validInput_2items() { + assertTrue( + "False result for (true, true)", + BooleanUtils.and(new boolean[] { true, true })); + + assertTrue( + "True result for (false, false)", + ! BooleanUtils.and(new boolean[] { false, false })); + + assertTrue( + "True result for (true, false)", + ! BooleanUtils.and(new boolean[] { true, false })); + + assertTrue( + "True result for (false, true)", + ! BooleanUtils.and(new boolean[] { false, true })); + } + + @Test + public void testAnd_primitive_validInput_3items() { + assertTrue( + "True result for (false, false, true)", + ! BooleanUtils.and(new boolean[] { false, false, true })); + + assertTrue( + "True result for (false, true, false)", + ! BooleanUtils.and(new boolean[] { false, true, false })); + + assertTrue( + "True result for (true, false, false)", + ! BooleanUtils.and(new boolean[] { true, false, false })); + + assertTrue( + "False result for (true, true, true)", + BooleanUtils.and(new boolean[] { true, true, true })); + + assertTrue( + "True result for (false, false)", + ! BooleanUtils.and(new boolean[] { false, false, false })); + + assertTrue( + "True result for (true, true, false)", + ! BooleanUtils.and(new boolean[] { true, true, false })); + + assertTrue( + "True result for (true, false, true)", + ! BooleanUtils.and(new boolean[] { true, false, true })); + + assertTrue( + "True result for (false, true, true)", + ! BooleanUtils.and(new boolean[] { false, true, true })); + } + + @Test(expected = IllegalArgumentException.class) + public void testAnd_object_nullInput() { + BooleanUtils.and((Boolean[]) null); + } + + @Test(expected = IllegalArgumentException.class) + public void testAnd_object_emptyInput() { + BooleanUtils.and(new Boolean[] {}); + } + + @Test(expected = IllegalArgumentException.class) + public void testAnd_object_nullElementInput() { + BooleanUtils.and(new Boolean[] {null}); + } + + @Test + public void testAnd_object_validInput_2items() { + assertTrue( + "False result for (true, true)", + BooleanUtils + .and(new Boolean[] { Boolean.TRUE, Boolean.TRUE }) + .booleanValue()); + + assertTrue( + "True result for (false, false)", + ! BooleanUtils + .and(new Boolean[] { Boolean.FALSE, Boolean.FALSE }) + .booleanValue()); + + assertTrue( + "True result for (true, false)", + ! BooleanUtils + .and(new Boolean[] { Boolean.TRUE, Boolean.FALSE }) + .booleanValue()); + + assertTrue( + "True result for (false, true)", + ! BooleanUtils + .and(new Boolean[] { Boolean.FALSE, Boolean.TRUE }) + .booleanValue()); + } + + @Test + public void testAnd_object_validInput_3items() { + assertTrue( + "True result for (false, false, true)", + ! BooleanUtils + .and( + new Boolean[] { + Boolean.FALSE, + Boolean.FALSE, + Boolean.TRUE }) + .booleanValue()); + + assertTrue( + "True result for (false, true, false)", + ! BooleanUtils + .and( + new Boolean[] { + Boolean.FALSE, + Boolean.TRUE, + Boolean.FALSE }) + .booleanValue()); + + assertTrue( + "True result for (true, false, false)", + ! BooleanUtils + .and( + new Boolean[] { + Boolean.TRUE, + Boolean.FALSE, + Boolean.FALSE }) + .booleanValue()); + + assertTrue( + "False result for (true, true, true)", + BooleanUtils + .and(new Boolean[] { Boolean.TRUE, Boolean.TRUE, Boolean.TRUE }) + .booleanValue()); + + assertTrue( + "True result for (false, false)", + ! BooleanUtils.and( + new Boolean[] { + Boolean.FALSE, + Boolean.FALSE, + Boolean.FALSE }) + .booleanValue()); + + assertTrue( + "True result for (true, true, false)", + ! BooleanUtils.and( + new Boolean[] { + Boolean.TRUE, + Boolean.TRUE, + Boolean.FALSE }) + .booleanValue()); + + assertTrue( + "True result for (true, false, true)", + ! BooleanUtils.and( + new Boolean[] { + Boolean.TRUE, + Boolean.FALSE, + Boolean.TRUE }) + .booleanValue()); + + assertTrue( + "True result for (false, true, true)", + ! BooleanUtils.and( + new Boolean[] { + Boolean.FALSE, + Boolean.TRUE, + Boolean.TRUE }) + .booleanValue()); + } + + // testOr + // ----------------------------------------------------------------------- + @Test(expected = IllegalArgumentException.class) + public void testOr_primitive_nullInput() { + BooleanUtils.or((boolean[]) null); + } + + @Test(expected = IllegalArgumentException.class) + public void testOr_primitive_emptyInput() { + BooleanUtils.or(new boolean[] {}); + } + + @Test + public void testOr_primitive_validInput_2items() { + assertTrue( + "False result for (true, true)", + BooleanUtils.or(new boolean[] { true, true })); + + assertTrue( + "True result for (false, false)", + ! BooleanUtils.or(new boolean[] { false, false })); + + assertTrue( + "False result for (true, false)", + BooleanUtils.or(new boolean[] { true, false })); + + assertTrue( + "False result for (false, true)", + BooleanUtils.or(new boolean[] { false, true })); + } + + @Test + public void testOr_primitive_validInput_3items() { + assertTrue( + "False result for (false, false, true)", + BooleanUtils.or(new boolean[] { false, false, true })); + + assertTrue( + "False result for (false, true, false)", + BooleanUtils.or(new boolean[] { false, true, false })); + + assertTrue( + "False result for (true, false, false)", + BooleanUtils.or(new boolean[] { true, false, false })); + + assertTrue( + "False result for (true, true, true)", + BooleanUtils.or(new boolean[] { true, true, true })); + + assertTrue( + "True result for (false, false)", + ! BooleanUtils.or(new boolean[] { false, false, false })); + + assertTrue( + "False result for (true, true, false)", + BooleanUtils.or(new boolean[] { true, true, false })); + + assertTrue( + "False result for (true, false, true)", + BooleanUtils.or(new boolean[] { true, false, true })); + + assertTrue( + "False result for (false, true, true)", + BooleanUtils.or(new boolean[] { false, true, true })); + + } + @Test(expected = IllegalArgumentException.class) + public void testOr_object_nullInput() { + BooleanUtils.or((Boolean[]) null); + } + + @Test(expected = IllegalArgumentException.class) + public void testOr_object_emptyInput() { + BooleanUtils.or(new Boolean[] {}); + } + + @Test(expected = IllegalArgumentException.class) + public void testOr_object_nullElementInput() { + BooleanUtils.or(new Boolean[] {null}); + } + + @Test + public void testOr_object_validInput_2items() { + assertTrue( + "False result for (true, true)", + BooleanUtils + .or(new Boolean[] { Boolean.TRUE, Boolean.TRUE }) + .booleanValue()); + + assertTrue( + "True result for (false, false)", + ! BooleanUtils + .or(new Boolean[] { Boolean.FALSE, Boolean.FALSE }) + .booleanValue()); + + assertTrue( + "False result for (true, false)", + BooleanUtils + .or(new Boolean[] { Boolean.TRUE, Boolean.FALSE }) + .booleanValue()); + + assertTrue( + "False result for (false, true)", + BooleanUtils + .or(new Boolean[] { Boolean.FALSE, Boolean.TRUE }) + .booleanValue()); + } + + @Test + public void testOr_object_validInput_3items() { + assertTrue( + "False result for (false, false, true)", + BooleanUtils + .or( + new Boolean[] { + Boolean.FALSE, + Boolean.FALSE, + Boolean.TRUE }) + .booleanValue()); + + assertTrue( + "False result for (false, true, false)", + BooleanUtils + .or( + new Boolean[] { + Boolean.FALSE, + Boolean.TRUE, + Boolean.FALSE }) + .booleanValue()); + + assertTrue( + "False result for (true, false, false)", + BooleanUtils + .or( + new Boolean[] { + Boolean.TRUE, + Boolean.FALSE, + Boolean.FALSE }) + .booleanValue()); + + assertTrue( + "False result for (true, true, true)", + BooleanUtils + .or(new Boolean[] { Boolean.TRUE, Boolean.TRUE, Boolean.TRUE }) + .booleanValue()); + + assertTrue( + "True result for (false, false)", + ! BooleanUtils.or( + new Boolean[] { + Boolean.FALSE, + Boolean.FALSE, + Boolean.FALSE }) + .booleanValue()); + + assertTrue( + "False result for (true, true, false)", + BooleanUtils.or( + new Boolean[] { + Boolean.TRUE, + Boolean.TRUE, + Boolean.FALSE }) + .booleanValue()); + + assertTrue( + "False result for (true, false, true)", + BooleanUtils.or( + new Boolean[] { + Boolean.TRUE, + Boolean.FALSE, + Boolean.TRUE }) + .booleanValue()); + + assertTrue( + "False result for (false, true, true)", + BooleanUtils.or( + new Boolean[] { + Boolean.FALSE, + Boolean.TRUE, + Boolean.TRUE }) + .booleanValue()); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/CharEncoding.java b/ApacheCommonsLang/org/apache/commons/lang3/CharEncoding.java new file mode 100644 index 0000000..4b896e1 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/CharEncoding.java @@ -0,0 +1,105 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.lang3; + +import java.nio.charset.Charset; +import java.nio.charset.IllegalCharsetNameException; + +/** + *

Character encoding names required of every implementation of the Java platform.

+ * + *

According to JRE character + * encoding names:

+ * + *

Every implementation of the Java platform is required to support the following character encodings. + * Consult the release documentation for your implementation to see if any other encodings are supported. + *

+ * + * @see JRE character encoding names + * @since 2.1 + * @version $Id: CharEncoding.java 1088899 2011-04-05 05:31:27Z bayard $ + */ +public class CharEncoding { + + /** + *

ISO Latin Alphabet #1, also known as ISO-LATIN-1.

+ * + *

Every implementation of the Java platform is required to support this character encoding.

+ */ + public static final String ISO_8859_1 = "ISO-8859-1"; + + /** + *

Seven-bit ASCII, also known as ISO646-US, also known as the Basic Latin block + * of the Unicode character set.

+ * + *

Every implementation of the Java platform is required to support this character encoding.

+ */ + public static final String US_ASCII = "US-ASCII"; + + /** + *

Sixteen-bit Unicode Transformation Format, byte order specified by a mandatory initial + * byte-order mark (either order accepted on input, big-endian used on output).

+ * + *

Every implementation of the Java platform is required to support this character encoding.

+ */ + public static final String UTF_16 = "UTF-16"; + + /** + *

Sixteen-bit Unicode Transformation Format, big-endian byte order.

+ * + *

Every implementation of the Java platform is required to support this character encoding.

+ */ + public static final String UTF_16BE = "UTF-16BE"; + + /** + *

Sixteen-bit Unicode Transformation Format, little-endian byte order.

+ * + *

Every implementation of the Java platform is required to support this character encoding.

+ */ + public static final String UTF_16LE = "UTF-16LE"; + + /** + *

Eight-bit Unicode Transformation Format.

+ * + *

Every implementation of the Java platform is required to support this character encoding.

+ */ + public static final String UTF_8 = "UTF-8"; + + //----------------------------------------------------------------------- + /** + *

Returns whether the named charset is supported.

+ * + *

This is similar to + * java.nio.charset.Charset.isSupported(String) but handles more formats

+ * + * @param name the name of the requested charset; may be either a canonical name or an alias, null returns false + * @return {@code true} if the charset is available in the current Java virtual machine + */ + public static boolean isSupported(String name) { + if (name == null) { + return false; + } + try { + return Charset.isSupported(name); + } catch (IllegalCharsetNameException ex) { + return false; + } + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/CharEncodingTest.java b/ApacheCommonsLang/org/apache/commons/lang3/CharEncodingTest.java new file mode 100644 index 0000000..722cade --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/CharEncodingTest.java @@ -0,0 +1,103 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.lang3; + +import static org.apache.commons.lang3.JavaVersion.JAVA_1_1; +import static org.apache.commons.lang3.JavaVersion.JAVA_1_2; +import static org.apache.commons.lang3.JavaVersion.JAVA_1_3; +import junit.framework.TestCase; + +/** + * Tests CharEncoding. + * + * @see CharEncoding + * @version $Id: CharEncodingTest.java 1088899 2011-04-05 05:31:27Z bayard $ + */ +public class CharEncodingTest extends TestCase { + + private void assertSupportedEncoding(String name) { + assertTrue("Encoding should be supported: " + name, CharEncoding.isSupported(name)); + } + + /** + * The class can be instantiated. + */ + public void testConstructor() { + new CharEncoding(); + } + + public void testMustBeSupportedJava1_3_1() { + if (SystemUtils.isJavaVersionAtLeast(JAVA_1_3)) { + this.assertSupportedEncoding(CharEncoding.ISO_8859_1); + this.assertSupportedEncoding(CharEncoding.US_ASCII); + this.assertSupportedEncoding(CharEncoding.UTF_16); + this.assertSupportedEncoding(CharEncoding.UTF_16BE); + this.assertSupportedEncoding(CharEncoding.UTF_16LE); + this.assertSupportedEncoding(CharEncoding.UTF_8); + } else { + this.warn("Java 1.3 tests not run since the current version is " + SystemUtils.JAVA_SPECIFICATION_VERSION); + } + } + + public void testSupported() { + assertTrue(CharEncoding.isSupported("UTF8")); + assertTrue(CharEncoding.isSupported("UTF-8")); + assertTrue(CharEncoding.isSupported("ASCII")); + } + + public void testNotSupported() { + assertFalse(CharEncoding.isSupported(null)); + assertFalse(CharEncoding.isSupported("")); + assertFalse(CharEncoding.isSupported(" ")); + assertFalse(CharEncoding.isSupported("\t\r\n")); + assertFalse(CharEncoding.isSupported("DOESNOTEXIST")); + assertFalse(CharEncoding.isSupported("this is not a valid encoding name")); + } + + public void testWorksOnJava1_1_8() { + // + // In this test, I simply deleted the encodings from the 1.3.1 list. + // The Javadoc do not specify which encodings are required. + // + if (SystemUtils.isJavaVersionAtLeast(JAVA_1_1)) { + this.assertSupportedEncoding(CharEncoding.ISO_8859_1); + this.assertSupportedEncoding(CharEncoding.US_ASCII); + this.assertSupportedEncoding(CharEncoding.UTF_8); + } else { + this.warn("Java 1.1 tests not run since the current version is " + SystemUtils.JAVA_SPECIFICATION_VERSION); + } + } + + public void testWorksOnJava1_2_2() { + // + // In this test, I simply deleted the encodings from the 1.3.1 list. + // The Javadoc do not specify which encodings are required. + // + if (SystemUtils.isJavaVersionAtLeast(JAVA_1_2)) { + this.assertSupportedEncoding(CharEncoding.ISO_8859_1); + this.assertSupportedEncoding(CharEncoding.US_ASCII); + this.assertSupportedEncoding(CharEncoding.UTF_8); + } else { + this.warn("Java 1.2 tests not run since the current version is " + SystemUtils.JAVA_SPECIFICATION_VERSION); + } + } + + void warn(String msg) { + System.err.println(msg); + } +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/CharRange.java b/ApacheCommonsLang/org/apache/commons/lang3/CharRange.java new file mode 100644 index 0000000..cc0495e --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/CharRange.java @@ -0,0 +1,360 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3; + +import java.io.Serializable; +import java.util.Iterator; +import java.util.NoSuchElementException; + +/** + *

A contiguous range of characters, optionally negated.

+ * + *

Instances are immutable.

+ * + *

#ThreadSafe#

+ * @since 1.0 + * @version $Id: CharRange.java 1309977 2012-04-05 17:53:39Z ggregory $ + */ +// TODO: This is no longer public and will be removed later as CharSet is moved +// to depend on Range. +final class CharRange implements Iterable, Serializable { + + /** + * Required for serialization support. Lang version 2.0. + * + * @see java.io.Serializable + */ + private static final long serialVersionUID = 8270183163158333422L; + + /** The first character, inclusive, in the range. */ + private final char start; + /** The last character, inclusive, in the range. */ + private final char end; + /** True if the range is everything except the characters specified. */ + private final boolean negated; + + /** Cached toString. */ + private transient String iToString; + + /** + *

Constructs a {@code CharRange} over a set of characters, + * optionally negating the range.

+ * + *

A negated range includes everything except that defined by the + * start and end characters.

+ * + *

If start and end are in the wrong order, they are reversed. + * Thus {@code a-e} is the same as {@code e-a}.

+ * + * @param start first character, inclusive, in this range + * @param end last character, inclusive, in this range + * @param negated true to express everything except the range + */ + private CharRange(char start, char end, boolean negated) { + super(); + if (start > end) { + char temp = start; + start = end; + end = temp; + } + + this.start = start; + this.end = end; + this.negated = negated; + } + + /** + *

Constructs a {@code CharRange} over a single character.

+ * + * @param ch only character in this range + * @return the new CharRange object + * @see CharRange#CharRange(char, char, boolean) + * @since 2.5 + */ + public static CharRange is(char ch) { + return new CharRange(ch, ch, false); + } + + /** + *

Constructs a negated {@code CharRange} over a single character.

+ * + * @param ch only character in this range + * @return the new CharRange object + * @see CharRange#CharRange(char, char, boolean) + * @since 2.5 + */ + public static CharRange isNot(char ch) { + return new CharRange(ch, ch, true); + } + + /** + *

Constructs a {@code CharRange} over a set of characters.

+ * + * @param start first character, inclusive, in this range + * @param end last character, inclusive, in this range + * @return the new CharRange object + * @see CharRange#CharRange(char, char, boolean) + * @since 2.5 + */ + public static CharRange isIn(char start, char end) { + return new CharRange(start, end, false); + } + + /** + *

Constructs a negated {@code CharRange} over a set of characters.

+ * + * @param start first character, inclusive, in this range + * @param end last character, inclusive, in this range + * @return the new CharRange object + * @see CharRange#CharRange(char, char, boolean) + * @since 2.5 + */ + public static CharRange isNotIn(char start, char end) { + return new CharRange(start, end, true); + } + + // Accessors + //----------------------------------------------------------------------- + /** + *

Gets the start character for this character range.

+ * + * @return the start char (inclusive) + */ + public char getStart() { + return this.start; + } + + /** + *

Gets the end character for this character range.

+ * + * @return the end char (inclusive) + */ + public char getEnd() { + return this.end; + } + + /** + *

Is this {@code CharRange} negated.

+ * + *

A negated range includes everything except that defined by the + * start and end characters.

+ * + * @return {@code true} if negated + */ + public boolean isNegated() { + return negated; + } + + // Contains + //----------------------------------------------------------------------- + /** + *

Is the character specified contained in this range.

+ * + * @param ch the character to check + * @return {@code true} if this range contains the input character + */ + public boolean contains(char ch) { + return (ch >= start && ch <= end) != negated; + } + + /** + *

Are all the characters of the passed in range contained in + * this range.

+ * + * @param range the range to check against + * @return {@code true} if this range entirely contains the input range + * @throws IllegalArgumentException if {@code null} input + */ + public boolean contains(CharRange range) { + if (range == null) { + throw new IllegalArgumentException("The Range must not be null"); + } + if (negated) { + if (range.negated) { + return start >= range.start && end <= range.end; + } + return range.end < start || range.start > end; + } + if (range.negated) { + return start == 0 && end == Character.MAX_VALUE; + } + return start <= range.start && end >= range.end; + } + + // Basics + //----------------------------------------------------------------------- + /** + *

Compares two CharRange objects, returning true if they represent + * exactly the same range of characters defined in the same way.

+ * + * @param obj the object to compare to + * @return true if equal + */ + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj instanceof CharRange == false) { + return false; + } + CharRange other = (CharRange) obj; + return start == other.start && end == other.end && negated == other.negated; + } + + /** + *

Gets a hashCode compatible with the equals method.

+ * + * @return a suitable hashCode + */ + @Override + public int hashCode() { + return 83 + start + 7 * end + (negated ? 1 : 0); + } + + /** + *

Gets a string representation of the character range.

+ * + * @return string representation of this range + */ + @Override + public String toString() { + if (iToString == null) { + StringBuilder buf = new StringBuilder(4); + if (isNegated()) { + buf.append('^'); + } + buf.append(start); + if (start != end) { + buf.append('-'); + buf.append(end); + } + iToString = buf.toString(); + } + return iToString; + } + + // Expansions + //----------------------------------------------------------------------- + /** + *

Returns an iterator which can be used to walk through the characters described by this range.

+ * + *

#NotThreadSafe# the iterator is not thread-safe

+ * @return an iterator to the chars represented by this range + * @since 2.5 + */ + @Override + public Iterator iterator() { + return new CharacterIterator(this); + } + + /** + * Character {@link Iterator}. + *

#NotThreadSafe#

+ */ + private static class CharacterIterator implements Iterator { + /** The current character */ + private char current; + + private final CharRange range; + private boolean hasNext; + + /** + * Construct a new iterator for the character range. + * + * @param r The character range + */ + private CharacterIterator(CharRange r) { + range = r; + hasNext = true; + + if (range.negated) { + if (range.start == 0) { + if (range.end == Character.MAX_VALUE) { + // This range is an empty set + hasNext = false; + } else { + current = (char) (range.end + 1); + } + } else { + current = 0; + } + } else { + current = range.start; + } + } + + /** + * Prepare the next character in the range. + */ + private void prepareNext() { + if (range.negated) { + if (current == Character.MAX_VALUE) { + hasNext = false; + } else if (current + 1 == range.start) { + if (range.end == Character.MAX_VALUE) { + hasNext = false; + } else { + current = (char) (range.end + 1); + } + } else { + current = (char) (current + 1); + } + } else if (current < range.end) { + current = (char) (current + 1); + } else { + hasNext = false; + } + } + + /** + * Has the iterator not reached the end character yet? + * + * @return {@code true} if the iterator has yet to reach the character date + */ + @Override + public boolean hasNext() { + return hasNext; + } + + /** + * Return the next character in the iteration + * + * @return {@code Character} for the next character + */ + @Override + public Character next() { + if (hasNext == false) { + throw new NoSuchElementException(); + } + char cur = current; + prepareNext(); + return Character.valueOf(cur); + } + + /** + * Always throws UnsupportedOperationException. + * + * @throws UnsupportedOperationException + * @see java.util.Iterator#remove() + */ + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + } +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/CharRangeTest.java b/ApacheCommonsLang/org/apache/commons/lang3/CharRangeTest.java new file mode 100644 index 0000000..99fbe8d --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/CharRangeTest.java @@ -0,0 +1,383 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.commons.lang3; + +import java.lang.reflect.Modifier; +import java.util.Iterator; +import java.util.NoSuchElementException; + +import junit.framework.TestCase; + +/** + * Unit tests {@link org.apache.commons.lang3.CharRange}. + * + * @version $Id: CharRangeTest.java 1090427 2011-04-08 20:17:10Z bayard $ + */ +public class CharRangeTest extends TestCase { + + public CharRangeTest(String name) { + super(name); + } + + //----------------------------------------------------------------------- + public void testClass() { + // class changed to non-public in 3.0 + assertEquals(false, Modifier.isPublic(CharRange.class.getModifiers())); + assertEquals(true, Modifier.isFinal(CharRange.class.getModifiers())); + } + + //----------------------------------------------------------------------- + public void testConstructorAccessors_is() { + CharRange rangea = CharRange.is('a'); + assertEquals('a', rangea.getStart()); + assertEquals('a', rangea.getEnd()); + assertEquals(false, rangea.isNegated()); + assertEquals("a", rangea.toString()); + } + + public void testConstructorAccessors_isNot() { + CharRange rangea = CharRange.isNot('a'); + assertEquals('a', rangea.getStart()); + assertEquals('a', rangea.getEnd()); + assertEquals(true, rangea.isNegated()); + assertEquals("^a", rangea.toString()); + } + + public void testConstructorAccessors_isIn_Same() { + CharRange rangea = CharRange.isIn('a', 'a'); + assertEquals('a', rangea.getStart()); + assertEquals('a', rangea.getEnd()); + assertEquals(false, rangea.isNegated()); + assertEquals("a", rangea.toString()); + } + + public void testConstructorAccessors_isIn_Normal() { + CharRange rangea = CharRange.isIn('a', 'e'); + assertEquals('a', rangea.getStart()); + assertEquals('e', rangea.getEnd()); + assertEquals(false, rangea.isNegated()); + assertEquals("a-e", rangea.toString()); + } + + public void testConstructorAccessors_isIn_Reversed() { + CharRange rangea = CharRange.isIn('e', 'a'); + assertEquals('a', rangea.getStart()); + assertEquals('e', rangea.getEnd()); + assertEquals(false, rangea.isNegated()); + assertEquals("a-e", rangea.toString()); + } + + public void testConstructorAccessors_isNotIn_Same() { + CharRange rangea = CharRange.isNotIn('a', 'a'); + assertEquals('a', rangea.getStart()); + assertEquals('a', rangea.getEnd()); + assertEquals(true, rangea.isNegated()); + assertEquals("^a", rangea.toString()); + } + + public void testConstructorAccessors_isNotIn_Normal() { + CharRange rangea = CharRange.isNotIn('a', 'e'); + assertEquals('a', rangea.getStart()); + assertEquals('e', rangea.getEnd()); + assertEquals(true, rangea.isNegated()); + assertEquals("^a-e", rangea.toString()); + } + + public void testConstructorAccessors_isNotIn_Reversed() { + CharRange rangea = CharRange.isNotIn('e', 'a'); + assertEquals('a', rangea.getStart()); + assertEquals('e', rangea.getEnd()); + assertEquals(true, rangea.isNegated()); + assertEquals("^a-e", rangea.toString()); + } + + //----------------------------------------------------------------------- + public void testEquals_Object() { + CharRange rangea = CharRange.is('a'); + CharRange rangeae = CharRange.isIn('a', 'e'); + CharRange rangenotbf = CharRange.isIn('b', 'f'); + + assertEquals(false, rangea.equals(null)); + + assertEquals(true, rangea.equals(rangea)); + assertEquals(true, rangea.equals(CharRange.is('a'))); + assertEquals(true, rangeae.equals(rangeae)); + assertEquals(true, rangeae.equals(CharRange.isIn('a', 'e'))); + assertEquals(true, rangenotbf.equals(rangenotbf)); + assertEquals(true, rangenotbf.equals(CharRange.isIn('b', 'f'))); + + assertEquals(false, rangea.equals(rangeae)); + assertEquals(false, rangea.equals(rangenotbf)); + assertEquals(false, rangeae.equals(rangea)); + assertEquals(false, rangeae.equals(rangenotbf)); + assertEquals(false, rangenotbf.equals(rangea)); + assertEquals(false, rangenotbf.equals(rangeae)); + } + + public void testHashCode() { + CharRange rangea = CharRange.is('a'); + CharRange rangeae = CharRange.isIn('a', 'e'); + CharRange rangenotbf = CharRange.isIn('b', 'f'); + + assertEquals(true, rangea.hashCode() == rangea.hashCode()); + assertEquals(true, rangea.hashCode() == CharRange.is('a').hashCode()); + assertEquals(true, rangeae.hashCode() == rangeae.hashCode()); + assertEquals(true, rangeae.hashCode() == CharRange.isIn('a', 'e').hashCode()); + assertEquals(true, rangenotbf.hashCode() == rangenotbf.hashCode()); + assertEquals(true, rangenotbf.hashCode() == CharRange.isIn('b', 'f').hashCode()); + + assertEquals(false, rangea.hashCode() == rangeae.hashCode()); + assertEquals(false, rangea.hashCode() == rangenotbf.hashCode()); + assertEquals(false, rangeae.hashCode() == rangea.hashCode()); + assertEquals(false, rangeae.hashCode() == rangenotbf.hashCode()); + assertEquals(false, rangenotbf.hashCode() == rangea.hashCode()); + assertEquals(false, rangenotbf.hashCode() == rangeae.hashCode()); + } + + //----------------------------------------------------------------------- + public void testContains_Char() { + CharRange range = CharRange.is('c'); + assertEquals(false, range.contains('b')); + assertEquals(true, range.contains('c')); + assertEquals(false, range.contains('d')); + assertEquals(false, range.contains('e')); + + range = CharRange.isIn('c', 'd'); + assertEquals(false, range.contains('b')); + assertEquals(true, range.contains('c')); + assertEquals(true, range.contains('d')); + assertEquals(false, range.contains('e')); + + range = CharRange.isIn('d', 'c'); + assertEquals(false, range.contains('b')); + assertEquals(true, range.contains('c')); + assertEquals(true, range.contains('d')); + assertEquals(false, range.contains('e')); + + range = CharRange.isNotIn('c', 'd'); + assertEquals(true, range.contains('b')); + assertEquals(false, range.contains('c')); + assertEquals(false, range.contains('d')); + assertEquals(true, range.contains('e')); + assertEquals(true, range.contains((char) 0)); + assertEquals(true, range.contains(Character.MAX_VALUE)); + } + + //----------------------------------------------------------------------- + public void testContains_Charrange() { + CharRange a = CharRange.is('a'); + CharRange b = CharRange.is('b'); + CharRange c = CharRange.is('c'); + CharRange c2 = CharRange.is('c'); + CharRange d = CharRange.is('d'); + CharRange e = CharRange.is('e'); + CharRange cd = CharRange.isIn('c', 'd'); + CharRange bd = CharRange.isIn('b', 'd'); + CharRange bc = CharRange.isIn('b', 'c'); + CharRange ab = CharRange.isIn('a', 'b'); + CharRange de = CharRange.isIn('d', 'e'); + CharRange ef = CharRange.isIn('e', 'f'); + CharRange ae = CharRange.isIn('a', 'e'); + + // normal/normal + assertEquals(false, c.contains(b)); + assertEquals(true, c.contains(c)); + assertEquals(true, c.contains(c2)); + assertEquals(false, c.contains(d)); + + assertEquals(false, c.contains(cd)); + assertEquals(false, c.contains(bd)); + assertEquals(false, c.contains(bc)); + assertEquals(false, c.contains(ab)); + assertEquals(false, c.contains(de)); + + assertEquals(true, cd.contains(c)); + assertEquals(true, bd.contains(c)); + assertEquals(true, bc.contains(c)); + assertEquals(false, ab.contains(c)); + assertEquals(false, de.contains(c)); + + assertEquals(true, ae.contains(b)); + assertEquals(true, ae.contains(ab)); + assertEquals(true, ae.contains(bc)); + assertEquals(true, ae.contains(cd)); + assertEquals(true, ae.contains(de)); + + CharRange notb = CharRange.isNot('b'); + CharRange notc = CharRange.isNot('c'); + CharRange notd = CharRange.isNot('d'); + CharRange notab = CharRange.isNotIn('a', 'b'); + CharRange notbc = CharRange.isNotIn('b', 'c'); + CharRange notbd = CharRange.isNotIn('b', 'd'); + CharRange notcd = CharRange.isNotIn('c', 'd'); + CharRange notde = CharRange.isNotIn('d', 'e'); + CharRange notae = CharRange.isNotIn('a', 'e'); + CharRange all = CharRange.isIn((char) 0, Character.MAX_VALUE); + CharRange allbutfirst = CharRange.isIn((char) 1, Character.MAX_VALUE); + + // normal/negated + assertEquals(false, c.contains(notc)); + assertEquals(false, c.contains(notbd)); + assertEquals(true, all.contains(notc)); + assertEquals(true, all.contains(notbd)); + assertEquals(false, allbutfirst.contains(notc)); + assertEquals(false, allbutfirst.contains(notbd)); + + // negated/normal + assertEquals(true, notc.contains(a)); + assertEquals(true, notc.contains(b)); + assertEquals(false, notc.contains(c)); + assertEquals(true, notc.contains(d)); + assertEquals(true, notc.contains(e)); + + assertEquals(true, notc.contains(ab)); + assertEquals(false, notc.contains(bc)); + assertEquals(false, notc.contains(bd)); + assertEquals(false, notc.contains(cd)); + assertEquals(true, notc.contains(de)); + assertEquals(false, notc.contains(ae)); + assertEquals(false, notc.contains(all)); + assertEquals(false, notc.contains(allbutfirst)); + + assertEquals(true, notbd.contains(a)); + assertEquals(false, notbd.contains(b)); + assertEquals(false, notbd.contains(c)); + assertEquals(false, notbd.contains(d)); + assertEquals(true, notbd.contains(e)); + + assertEquals(true, notcd.contains(ab)); + assertEquals(false, notcd.contains(bc)); + assertEquals(false, notcd.contains(bd)); + assertEquals(false, notcd.contains(cd)); + assertEquals(false, notcd.contains(de)); + assertEquals(false, notcd.contains(ae)); + assertEquals(true, notcd.contains(ef)); + assertEquals(false, notcd.contains(all)); + assertEquals(false, notcd.contains(allbutfirst)); + + // negated/negated + assertEquals(false, notc.contains(notb)); + assertEquals(true, notc.contains(notc)); + assertEquals(false, notc.contains(notd)); + + assertEquals(false, notc.contains(notab)); + assertEquals(true, notc.contains(notbc)); + assertEquals(true, notc.contains(notbd)); + assertEquals(true, notc.contains(notcd)); + assertEquals(false, notc.contains(notde)); + + assertEquals(false, notbd.contains(notb)); + assertEquals(false, notbd.contains(notc)); + assertEquals(false, notbd.contains(notd)); + + assertEquals(false, notbd.contains(notab)); + assertEquals(false, notbd.contains(notbc)); + assertEquals(true, notbd.contains(notbd)); + assertEquals(false, notbd.contains(notcd)); + assertEquals(false, notbd.contains(notde)); + assertEquals(true, notbd.contains(notae)); + } + + public void testContainsNullArg() { + CharRange range = CharRange.is('a'); + try { + @SuppressWarnings("unused") + boolean contains = range.contains(null); + } catch(IllegalArgumentException e) { + assertEquals("The Range must not be null", e.getMessage()); + } + } + + public void testIterator() { + CharRange a = CharRange.is('a'); + CharRange ad = CharRange.isIn('a', 'd'); + CharRange nota = CharRange.isNot('a'); + CharRange emptySet = CharRange.isNotIn((char) 0, Character.MAX_VALUE); + CharRange notFirst = CharRange.isNotIn((char) 1, Character.MAX_VALUE); + CharRange notLast = CharRange.isNotIn((char) 0, (char) (Character.MAX_VALUE - 1)); + + Iterator aIt = a.iterator(); + assertNotNull(aIt); + assertTrue(aIt.hasNext()); + assertEquals(Character.valueOf('a'), aIt.next()); + assertFalse(aIt.hasNext()); + + Iterator adIt = ad.iterator(); + assertNotNull(adIt); + assertTrue(adIt.hasNext()); + assertEquals(Character.valueOf('a'), adIt.next()); + assertEquals(Character.valueOf('b'), adIt.next()); + assertEquals(Character.valueOf('c'), adIt.next()); + assertEquals(Character.valueOf('d'), adIt.next()); + assertFalse(adIt.hasNext()); + + Iterator notaIt = nota.iterator(); + assertNotNull(notaIt); + assertTrue(notaIt.hasNext()); + while (notaIt.hasNext()) { + Character c = notaIt.next(); + assertFalse('a' == c.charValue()); + } + + Iterator emptySetIt = emptySet.iterator(); + assertNotNull(emptySetIt); + assertFalse(emptySetIt.hasNext()); + try { + emptySetIt.next(); + fail("Should throw NoSuchElementException"); + } catch (NoSuchElementException e) { + assertTrue(true); + } + + Iterator notFirstIt = notFirst.iterator(); + assertNotNull(notFirstIt); + assertTrue(notFirstIt.hasNext()); + assertEquals(Character.valueOf((char) 0), notFirstIt.next()); + assertFalse(notFirstIt.hasNext()); + try { + notFirstIt.next(); + fail("Should throw NoSuchElementException"); + } catch (NoSuchElementException e) { + assertTrue(true); + } + + Iterator notLastIt = notLast.iterator(); + assertNotNull(notLastIt); + assertTrue(notLastIt.hasNext()); + assertEquals(Character.valueOf(Character.MAX_VALUE), notLastIt.next()); + assertFalse(notLastIt.hasNext()); + try { + notLastIt.next(); + fail("Should throw NoSuchElementException"); + } catch (NoSuchElementException e) { + assertTrue(true); + } + } + + //----------------------------------------------------------------------- + public void testSerialization() { + CharRange range = CharRange.is('a'); + assertEquals(range, SerializationUtils.clone(range)); + range = CharRange.isIn('a', 'e'); + assertEquals(range, SerializationUtils.clone(range)); + range = CharRange.isNotIn('a', 'e'); + assertEquals(range, SerializationUtils.clone(range)); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/CharSequenceUtils.java b/ApacheCommonsLang/org/apache/commons/lang3/CharSequenceUtils.java new file mode 100644 index 0000000..40e4923 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/CharSequenceUtils.java @@ -0,0 +1,197 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3; + +/** + *

Operations on {@link java.lang.CharSequence} that are + * {@code null} safe.

+ * + * @see java.lang.CharSequence + * @since 3.0 + * @version $Id: CharSequenceUtils.java 1199894 2011-11-09 17:53:59Z ggregory $ + */ +public class CharSequenceUtils { + + /** + *

{@code CharSequenceUtils} instances should NOT be constructed in + * standard programming.

+ * + *

This constructor is public to permit tools that require a JavaBean + * instance to operate.

+ */ + public CharSequenceUtils() { + super(); + } + + //----------------------------------------------------------------------- + /** + *

Returns a new {@code CharSequence} that is a subsequence of this + * sequence starting with the {@code char} value at the specified index.

+ * + *

This provides the {@code CharSequence} equivalent to {@link String#substring(int)}. + * The length (in {@code char}) of the returned sequence is {@code length() - start}, + * so if {@code start == end} then an empty sequence is returned.

+ * + * @param cs the specified subsequence, null returns null + * @param start the start index, inclusive, valid + * @return a new subsequence, may be null + * @throws IndexOutOfBoundsException if {@code start} is negative or if + * {@code start} is greater than {@code length()} + */ + public static CharSequence subSequence(CharSequence cs, int start) { + return cs == null ? null : cs.subSequence(start, cs.length()); + } + + //----------------------------------------------------------------------- + /** + *

Finds the first index in the {@code CharSequence} that matches the + * specified character.

+ * + * @param cs the {@code CharSequence} to be processed, not null + * @param searchChar the char to be searched for + * @param start the start index, negative starts at the string start + * @return the index where the search char was found, -1 if not found + */ + static int indexOf(CharSequence cs, int searchChar, int start) { + if (cs instanceof String) { + return ((String) cs).indexOf(searchChar, start); + } else { + int sz = cs.length(); + if (start < 0) { + start = 0; + } + for (int i = start; i < sz; i++) { + if (cs.charAt(i) == searchChar) { + return i; + } + } + return -1; + } + } + + /** + * Used by the indexOf(CharSequence methods) as a green implementation of indexOf. + * + * @param cs the {@code CharSequence} to be processed + * @param searchChar the {@code CharSequence} to be searched for + * @param start the start index + * @return the index where the search sequence was found + */ + static int indexOf(CharSequence cs, CharSequence searchChar, int start) { + return cs.toString().indexOf(searchChar.toString(), start); +// if (cs instanceof String && searchChar instanceof String) { +// // TODO: Do we assume searchChar is usually relatively small; +// // If so then calling toString() on it is better than reverting to +// // the green implementation in the else block +// return ((String) cs).indexOf((String) searchChar, start); +// } else { +// // TODO: Implement rather than convert to String +// return cs.toString().indexOf(searchChar.toString(), start); +// } + } + + /** + *

Finds the last index in the {@code CharSequence} that matches the + * specified character.

+ * + * @param cs the {@code CharSequence} to be processed + * @param searchChar the char to be searched for + * @param start the start index, negative returns -1, beyond length starts at end + * @return the index where the search char was found, -1 if not found + */ + static int lastIndexOf(CharSequence cs, int searchChar, int start) { + if (cs instanceof String) { + return ((String) cs).lastIndexOf(searchChar, start); + } else { + int sz = cs.length(); + if (start < 0) { + return -1; + } + if (start >= sz) { + start = sz - 1; + } + for (int i = start; i >= 0; --i) { + if (cs.charAt(i) == searchChar) { + return i; + } + } + return -1; + } + } + + /** + * Used by the lastIndexOf(CharSequence methods) as a green implementation of lastIndexOf + * + * @param cs the {@code CharSequence} to be processed + * @param searchChar the {@code CharSequence} to be searched for + * @param start the start index + * @return the index where the search sequence was found + */ + static int lastIndexOf(CharSequence cs, CharSequence searchChar, int start) { + return cs.toString().lastIndexOf(searchChar.toString(), start); +// if (cs instanceof String && searchChar instanceof String) { +// // TODO: Do we assume searchChar is usually relatively small; +// // If so then calling toString() on it is better than reverting to +// // the green implementation in the else block +// return ((String) cs).lastIndexOf((String) searchChar, start); +// } else { +// // TODO: Implement rather than convert to String +// return cs.toString().lastIndexOf(searchChar.toString(), start); +// } + } + + /** + * Green implementation of toCharArray. + * + * @param cs the {@code CharSequence} to be processed + * @return the resulting char array + */ + static char[] toCharArray(CharSequence cs) { + if (cs instanceof String) { + return ((String) cs).toCharArray(); + } else { + int sz = cs.length(); + char[] array = new char[cs.length()]; + for (int i = 0; i < sz; i++) { + array[i] = cs.charAt(i); + } + return array; + } + } + + /** + * Green implementation of regionMatches. + * + * @param cs the {@code CharSequence} to be processed + * @param ignoreCase whether or not to be case insensitive + * @param thisStart the index to start on the {@code cs} CharSequence + * @param substring the {@code CharSequence} to be looked for + * @param start the index to start on the {@code substring} CharSequence + * @param length character length of the region + * @return whether the region matched + */ + static boolean regionMatches(CharSequence cs, boolean ignoreCase, int thisStart, + CharSequence substring, int start, int length) { + if (cs instanceof String && substring instanceof String) { + return ((String) cs).regionMatches(ignoreCase, thisStart, (String) substring, start, length); + } else { + // TODO: Implement rather than convert to String + return cs.toString().regionMatches(ignoreCase, thisStart, substring.toString(), start, length); + } + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/CharSequenceUtilsTest.java b/ApacheCommonsLang/org/apache/commons/lang3/CharSequenceUtilsTest.java new file mode 100644 index 0000000..a8812e7 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/CharSequenceUtilsTest.java @@ -0,0 +1,75 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Modifier; + +import junit.framework.Assert; +import junit.framework.TestCase; + +/** + * Tests CharSequenceUtils + * + * @version $Id: CharSequenceUtilsTest.java 1066341 2011-02-02 06:21:53Z bayard $ + */ +public class CharSequenceUtilsTest extends TestCase { + + //----------------------------------------------------------------------- + public void testConstructor() { + assertNotNull(new CharSequenceUtils()); + Constructor[] cons = CharSequenceUtils.class.getDeclaredConstructors(); + assertEquals(1, cons.length); + assertEquals(true, Modifier.isPublic(cons[0].getModifiers())); + assertEquals(true, Modifier.isPublic(CharSequenceUtils.class.getModifiers())); + assertEquals(false, Modifier.isFinal(CharSequenceUtils.class.getModifiers())); + } + + //----------------------------------------------------------------------- + public void testSubSequence() { + // + // null input + // + Assert.assertEquals(null, CharSequenceUtils.subSequence(null, -1)); + Assert.assertEquals(null, CharSequenceUtils.subSequence(null, 0)); + Assert.assertEquals(null, CharSequenceUtils.subSequence(null, 1)); + // + // non-null input + // + Assert.assertEquals(StringUtils.EMPTY, CharSequenceUtils.subSequence(StringUtils.EMPTY, 0)); + Assert.assertEquals("012", CharSequenceUtils.subSequence("012", 0)); + Assert.assertEquals("12", CharSequenceUtils.subSequence("012", 1)); + Assert.assertEquals("2", CharSequenceUtils.subSequence("012", 2)); + Assert.assertEquals(StringUtils.EMPTY, CharSequenceUtils.subSequence("012", 3)); + // + // Exception expected + // + try { + Assert.assertEquals(null, CharSequenceUtils.subSequence(StringUtils.EMPTY, -1)); + Assert.fail("Expected " + IndexOutOfBoundsException.class.getName()); + } catch (IndexOutOfBoundsException e) { + // Expected + } + try { + Assert.assertEquals(null, CharSequenceUtils.subSequence(StringUtils.EMPTY, 1)); + Assert.fail("Expected " + IndexOutOfBoundsException.class.getName()); + } catch (IndexOutOfBoundsException e) { + // Expected + } + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/CharSet.java b/ApacheCommonsLang/org/apache/commons/lang3/CharSet.java new file mode 100644 index 0000000..b7c176f --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/CharSet.java @@ -0,0 +1,278 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3; + +import java.io.Serializable; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + *

A set of characters.

+ * + *

Instances are immutable, but instances of subclasses may not be.

+ * + *

#ThreadSafe#

+ * @since 1.0 + * @version $Id: CharSet.java 1199894 2011-11-09 17:53:59Z ggregory $ + */ +public class CharSet implements Serializable { + + /** + * Required for serialization support. Lang version 2.0. + * + * @see java.io.Serializable + */ + private static final long serialVersionUID = 5947847346149275958L; + + /** + * A CharSet defining no characters. + * @since 2.0 + */ + public static final CharSet EMPTY = new CharSet((String) null); + + /** + * A CharSet defining ASCII alphabetic characters "a-zA-Z". + * @since 2.0 + */ + public static final CharSet ASCII_ALPHA = new CharSet("a-zA-Z"); + + /** + * A CharSet defining ASCII alphabetic characters "a-z". + * @since 2.0 + */ + public static final CharSet ASCII_ALPHA_LOWER = new CharSet("a-z"); + + /** + * A CharSet defining ASCII alphabetic characters "A-Z". + * @since 2.0 + */ + public static final CharSet ASCII_ALPHA_UPPER = new CharSet("A-Z"); + + /** + * A CharSet defining ASCII alphabetic characters "0-9". + * @since 2.0 + */ + public static final CharSet ASCII_NUMERIC = new CharSet("0-9"); + + /** + * A Map of the common cases used in the factory. + * Subclasses can add more common patterns if desired + * @since 2.0 + */ + protected static final Map COMMON = Collections.synchronizedMap(new HashMap()); + + static { + COMMON.put(null, EMPTY); + COMMON.put("", EMPTY); + COMMON.put("a-zA-Z", ASCII_ALPHA); + COMMON.put("A-Za-z", ASCII_ALPHA); + COMMON.put("a-z", ASCII_ALPHA_LOWER); + COMMON.put("A-Z", ASCII_ALPHA_UPPER); + COMMON.put("0-9", ASCII_NUMERIC); + } + + /** The set of CharRange objects. */ + private final Set set = Collections.synchronizedSet(new HashSet()); + + //----------------------------------------------------------------------- + /** + *

Factory method to create a new CharSet using a special syntax.

+ * + *
    + *
  • {@code null} or empty string ("") + * - set containing no characters
  • + *
  • Single character, such as "a" + * - set containing just that character
  • + *
  • Multi character, such as "a-e" + * - set containing characters from one character to the other
  • + *
  • Negated, such as "^a" or "^a-e" + * - set containing all characters except those defined
  • + *
  • Combinations, such as "abe-g" + * - set containing all the characters from the individual sets
  • + *
+ * + *

The matching order is:

+ *
    + *
  1. Negated multi character range, such as "^a-e" + *
  2. Ordinary multi character range, such as "a-e" + *
  3. Negated single character, such as "^a" + *
  4. Ordinary single character, such as "a" + *
+ *

Matching works left to right. Once a match is found the + * search starts again from the next character.

+ * + *

If the same range is defined twice using the same syntax, only + * one range will be kept. + * Thus, "a-ca-c" creates only one range of "a-c".

+ * + *

If the start and end of a range are in the wrong order, + * they are reversed. Thus "a-e" is the same as "e-a". + * As a result, "a-ee-a" would create only one range, + * as the "a-e" and "e-a" are the same.

+ * + *

The set of characters represented is the union of the specified ranges.

+ * + *

All CharSet objects returned by this method will be immutable.

+ * + * @param setStrs Strings to merge into the set, may be null + * @return a CharSet instance + * @since 2.4 + */ + public static CharSet getInstance(String... setStrs) { + if (setStrs == null) { + return null; + } + if (setStrs.length == 1) { + CharSet common = COMMON.get(setStrs[0]); + if (common != null) { + return common; + } + } + return new CharSet(setStrs); + } + + //----------------------------------------------------------------------- + /** + *

Constructs a new CharSet using the set syntax. + * Each string is merged in with the set.

+ * + * @param set Strings to merge into the initial set + * @throws NullPointerException if set is {@code null} + */ + protected CharSet(String... set) { + super(); + int sz = set.length; + for (int i = 0; i < sz; i++) { + add(set[i]); + } + } + + //----------------------------------------------------------------------- + /** + *

Add a set definition string to the {@code CharSet}.

+ * + * @param str set definition string + */ + protected void add(String str) { + if (str == null) { + return; + } + + int len = str.length(); + int pos = 0; + while (pos < len) { + int remainder = len - pos; + if (remainder >= 4 && str.charAt(pos) == '^' && str.charAt(pos + 2) == '-') { + // negated range + set.add(CharRange.isNotIn(str.charAt(pos + 1), str.charAt(pos + 3))); + pos += 4; + } else if (remainder >= 3 && str.charAt(pos + 1) == '-') { + // range + set.add(CharRange.isIn(str.charAt(pos), str.charAt(pos + 2))); + pos += 3; + } else if (remainder >= 2 && str.charAt(pos) == '^') { + // negated char + set.add(CharRange.isNot(str.charAt(pos + 1))); + pos += 2; + } else { + // char + set.add(CharRange.is(str.charAt(pos))); + pos += 1; + } + } + } + + //----------------------------------------------------------------------- + /** + *

Gets the internal set as an array of CharRange objects.

+ * + * @return an array of immutable CharRange objects + * @since 2.0 + */ +// NOTE: This is no longer public as CharRange is no longer a public class. +// It may be replaced when CharSet moves to Range. + /*public*/ CharRange[] getCharRanges() { + return set.toArray(new CharRange[set.size()]); + } + + //----------------------------------------------------------------------- + /** + *

Does the {@code CharSet} contain the specified + * character {@code ch}.

+ * + * @param ch the character to check for + * @return {@code true} if the set contains the characters + */ + public boolean contains(char ch) { + for (CharRange range : set) { + if (range.contains(ch)) { + return true; + } + } + return false; + } + + // Basics + //----------------------------------------------------------------------- + /** + *

Compares two {@code CharSet} objects, returning true if they represent + * exactly the same set of characters defined in the same way.

+ * + *

The two sets {@code abc} and {@code a-c} are not + * equal according to this method.

+ * + * @param obj the object to compare to + * @return true if equal + * @since 2.0 + */ + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj instanceof CharSet == false) { + return false; + } + CharSet other = (CharSet) obj; + return set.equals(other.set); + } + + /** + *

Gets a hash code compatible with the equals method.

+ * + * @return a suitable hash code + * @since 2.0 + */ + @Override + public int hashCode() { + return 89 + set.hashCode(); + } + + /** + *

Gets a string representation of the set.

+ * + * @return string representation of the set + */ + @Override + public String toString() { + return set.toString(); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/CharSetTest.java b/ApacheCommonsLang/org/apache/commons/lang3/CharSetTest.java new file mode 100644 index 0000000..b2d430d --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/CharSetTest.java @@ -0,0 +1,454 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.commons.lang3; + +import java.lang.reflect.Modifier; + +import junit.framework.TestCase; + +/** + * Unit tests {@link org.apache.commons.lang3.CharSet}. + * + * @version $Id: CharSetTest.java 1088899 2011-04-05 05:31:27Z bayard $ + */ +public class CharSetTest extends TestCase { + + public CharSetTest(String name) { + super(name); + } + + //----------------------------------------------------------------------- + public void testClass() { + assertEquals(true, Modifier.isPublic(CharSet.class.getModifiers())); + assertEquals(false, Modifier.isFinal(CharSet.class.getModifiers())); + } + + //----------------------------------------------------------------------- + public void testGetInstance() { + assertSame(CharSet.EMPTY, CharSet.getInstance( (String) null)); + assertSame(CharSet.EMPTY, CharSet.getInstance("")); + assertSame(CharSet.ASCII_ALPHA, CharSet.getInstance("a-zA-Z")); + assertSame(CharSet.ASCII_ALPHA, CharSet.getInstance("A-Za-z")); + assertSame(CharSet.ASCII_ALPHA_LOWER, CharSet.getInstance("a-z")); + assertSame(CharSet.ASCII_ALPHA_UPPER, CharSet.getInstance("A-Z")); + assertSame(CharSet.ASCII_NUMERIC, CharSet.getInstance("0-9")); + } + + //----------------------------------------------------------------------- + public void testGetInstance_Stringarray() { + assertEquals(null, CharSet.getInstance((String[]) null)); + assertEquals("[]", CharSet.getInstance(new String[0]).toString()); + assertEquals("[]", CharSet.getInstance(new String[] {null}).toString()); + assertEquals("[a-e]", CharSet.getInstance(new String[] {"a-e"}).toString()); + } + + //----------------------------------------------------------------------- + public void testConstructor_String_simple() { + CharSet set; + CharRange[] array; + + set = CharSet.getInstance((String) null); + array = set.getCharRanges(); + assertEquals("[]", set.toString()); + assertEquals(0, array.length); + + set = CharSet.getInstance(""); + array = set.getCharRanges(); + assertEquals("[]", set.toString()); + assertEquals(0, array.length); + + set = CharSet.getInstance("a"); + array = set.getCharRanges(); + assertEquals("[a]", set.toString()); + assertEquals(1, array.length); + assertEquals("a", array[0].toString()); + + set = CharSet.getInstance("^a"); + array = set.getCharRanges(); + assertEquals("[^a]", set.toString()); + assertEquals(1, array.length); + assertEquals("^a", array[0].toString()); + + set = CharSet.getInstance("a-e"); + array = set.getCharRanges(); + assertEquals("[a-e]", set.toString()); + assertEquals(1, array.length); + assertEquals("a-e", array[0].toString()); + + set = CharSet.getInstance("^a-e"); + array = set.getCharRanges(); + assertEquals("[^a-e]", set.toString()); + assertEquals(1, array.length); + assertEquals("^a-e", array[0].toString()); + } + + public void testConstructor_String_combo() { + CharSet set; + CharRange[] array; + + set = CharSet.getInstance("abc"); + array = set.getCharRanges(); + assertEquals(3, array.length); + assertEquals(true, ArrayUtils.contains(array, CharRange.is('a'))); + assertEquals(true, ArrayUtils.contains(array, CharRange.is('b'))); + assertEquals(true, ArrayUtils.contains(array, CharRange.is('c'))); + + set = CharSet.getInstance("a-ce-f"); + array = set.getCharRanges(); + assertEquals(2, array.length); + assertEquals(true, ArrayUtils.contains(array, CharRange.isIn('a', 'c'))); + assertEquals(true, ArrayUtils.contains(array, CharRange.isIn('e', 'f'))); + + set = CharSet.getInstance("ae-f"); + array = set.getCharRanges(); + assertEquals(2, array.length); + assertEquals(true, ArrayUtils.contains(array, CharRange.is('a'))); + assertEquals(true, ArrayUtils.contains(array, CharRange.isIn('e', 'f'))); + + set = CharSet.getInstance("e-fa"); + array = set.getCharRanges(); + assertEquals(2, array.length); + assertEquals(true, ArrayUtils.contains(array, CharRange.is('a'))); + assertEquals(true, ArrayUtils.contains(array, CharRange.isIn('e', 'f'))); + + set = CharSet.getInstance("ae-fm-pz"); + array = set.getCharRanges(); + assertEquals(4, array.length); + assertEquals(true, ArrayUtils.contains(array, CharRange.is('a'))); + assertEquals(true, ArrayUtils.contains(array, CharRange.isIn('e', 'f'))); + assertEquals(true, ArrayUtils.contains(array, CharRange.isIn('m', 'p'))); + assertEquals(true, ArrayUtils.contains(array, CharRange.is('z'))); + } + + public void testConstructor_String_comboNegated() { + CharSet set; + CharRange[] array; + + set = CharSet.getInstance("^abc"); + array = set.getCharRanges(); + assertEquals(3, array.length); + assertEquals(true, ArrayUtils.contains(array, CharRange.isNot('a'))); + assertEquals(true, ArrayUtils.contains(array, CharRange.is('b'))); + assertEquals(true, ArrayUtils.contains(array, CharRange.is('c'))); + + set = CharSet.getInstance("b^ac"); + array = set.getCharRanges(); + assertEquals(3, array.length); + assertEquals(true, ArrayUtils.contains(array, CharRange.is('b'))); + assertEquals(true, ArrayUtils.contains(array, CharRange.isNot('a'))); + assertEquals(true, ArrayUtils.contains(array, CharRange.is('c'))); + + set = CharSet.getInstance("db^ac"); + array = set.getCharRanges(); + assertEquals(4, array.length); + assertEquals(true, ArrayUtils.contains(array, CharRange.is('d'))); + assertEquals(true, ArrayUtils.contains(array, CharRange.is('b'))); + assertEquals(true, ArrayUtils.contains(array, CharRange.isNot('a'))); + assertEquals(true, ArrayUtils.contains(array, CharRange.is('c'))); + + set = CharSet.getInstance("^b^a"); + array = set.getCharRanges(); + assertEquals(2, array.length); + assertEquals(true, ArrayUtils.contains(array, CharRange.isNot('b'))); + assertEquals(true, ArrayUtils.contains(array, CharRange.isNot('a'))); + + set = CharSet.getInstance("b^a-c^z"); + array = set.getCharRanges(); + assertEquals(3, array.length); + assertEquals(true, ArrayUtils.contains(array, CharRange.isNotIn('a', 'c'))); + assertEquals(true, ArrayUtils.contains(array, CharRange.isNot('z'))); + assertEquals(true, ArrayUtils.contains(array, CharRange.is('b'))); + } + + public void testConstructor_String_oddDash() { + CharSet set; + CharRange[] array; + + set = CharSet.getInstance("-"); + array = set.getCharRanges(); + assertEquals(1, array.length); + assertEquals(true, ArrayUtils.contains(array, CharRange.is('-'))); + + set = CharSet.getInstance("--"); + array = set.getCharRanges(); + assertEquals(1, array.length); + assertEquals(true, ArrayUtils.contains(array, CharRange.is('-'))); + + set = CharSet.getInstance("---"); + array = set.getCharRanges(); + assertEquals(1, array.length); + assertEquals(true, ArrayUtils.contains(array, CharRange.is('-'))); + + set = CharSet.getInstance("----"); + array = set.getCharRanges(); + assertEquals(1, array.length); + assertEquals(true, ArrayUtils.contains(array, CharRange.is('-'))); + + set = CharSet.getInstance("-a"); + array = set.getCharRanges(); + assertEquals(2, array.length); + assertEquals(true, ArrayUtils.contains(array, CharRange.is('-'))); + assertEquals(true, ArrayUtils.contains(array, CharRange.is('a'))); + + set = CharSet.getInstance("a-"); + array = set.getCharRanges(); + assertEquals(2, array.length); + assertEquals(true, ArrayUtils.contains(array, CharRange.is('a'))); + assertEquals(true, ArrayUtils.contains(array, CharRange.is('-'))); + + set = CharSet.getInstance("a--"); + array = set.getCharRanges(); + assertEquals(1, array.length); + assertEquals(true, ArrayUtils.contains(array, CharRange.isIn('a', '-'))); + + set = CharSet.getInstance("--a"); + array = set.getCharRanges(); + assertEquals(1, array.length); + assertEquals(true, ArrayUtils.contains(array, CharRange.isIn('-', 'a'))); + } + + public void testConstructor_String_oddNegate() { + CharSet set; + CharRange[] array; + set = CharSet.getInstance("^"); + array = set.getCharRanges(); + assertEquals(1, array.length); + assertEquals(true, ArrayUtils.contains(array, CharRange.is('^'))); // "^" + + set = CharSet.getInstance("^^"); + array = set.getCharRanges(); + assertEquals(1, array.length); + assertEquals(true, ArrayUtils.contains(array, CharRange.isNot('^'))); // "^^" + + set = CharSet.getInstance("^^^"); + array = set.getCharRanges(); + assertEquals(2, array.length); + assertEquals(true, ArrayUtils.contains(array, CharRange.isNot('^'))); // "^^" + assertEquals(true, ArrayUtils.contains(array, CharRange.is('^'))); // "^" + + set = CharSet.getInstance("^^^^"); + array = set.getCharRanges(); + assertEquals(1, array.length); + assertEquals(true, ArrayUtils.contains(array, CharRange.isNot('^'))); // "^^" x2 + + set = CharSet.getInstance("a^"); + array = set.getCharRanges(); + assertEquals(2, array.length); + assertEquals(true, ArrayUtils.contains(array, CharRange.is('a'))); // "a" + assertEquals(true, ArrayUtils.contains(array, CharRange.is('^'))); // "^" + + set = CharSet.getInstance("^a-"); + array = set.getCharRanges(); + assertEquals(2, array.length); + assertEquals(true, ArrayUtils.contains(array, CharRange.isNot('a'))); // "^a" + assertEquals(true, ArrayUtils.contains(array, CharRange.is('-'))); // "-" + + set = CharSet.getInstance("^^-c"); + array = set.getCharRanges(); + assertEquals(1, array.length); + assertEquals(true, ArrayUtils.contains(array, CharRange.isNotIn('^', 'c'))); // "^^-c" + + set = CharSet.getInstance("^c-^"); + array = set.getCharRanges(); + assertEquals(1, array.length); + assertEquals(true, ArrayUtils.contains(array, CharRange.isNotIn('c', '^'))); // "^c-^" + + set = CharSet.getInstance("^c-^d"); + array = set.getCharRanges(); + assertEquals(2, array.length); + assertEquals(true, ArrayUtils.contains(array, CharRange.isNotIn('c', '^'))); // "^c-^" + assertEquals(true, ArrayUtils.contains(array, CharRange.is('d'))); // "d" + + set = CharSet.getInstance("^^-"); + array = set.getCharRanges(); + assertEquals(2, array.length); + assertEquals(true, ArrayUtils.contains(array, CharRange.isNot('^'))); // "^^" + assertEquals(true, ArrayUtils.contains(array, CharRange.is('-'))); // "-" + } + + public void testConstructor_String_oddCombinations() { + CharSet set; + CharRange[] array = null; + + set = CharSet.getInstance("a-^c"); + array = set.getCharRanges(); + assertEquals(true, ArrayUtils.contains(array, CharRange.isIn('a', '^'))); // "a-^" + assertEquals(true, ArrayUtils.contains(array, CharRange.is('c'))); // "c" + assertEquals(false, set.contains('b')); + assertEquals(true, set.contains('^')); + assertEquals(true, set.contains('_')); // between ^ and a + assertEquals(true, set.contains('c')); + + set = CharSet.getInstance("^a-^c"); + array = set.getCharRanges(); + assertEquals(true, ArrayUtils.contains(array, CharRange.isNotIn('a', '^'))); // "^a-^" + assertEquals(true, ArrayUtils.contains(array, CharRange.is('c'))); // "c" + assertEquals(true, set.contains('b')); + assertEquals(false, set.contains('^')); + assertEquals(false, set.contains('_')); // between ^ and a + + set = CharSet.getInstance("a- ^-- "); //contains everything + array = set.getCharRanges(); + assertEquals(true, ArrayUtils.contains(array, CharRange.isIn('a', ' '))); // "a- " + assertEquals(true, ArrayUtils.contains(array, CharRange.isNotIn('-', ' '))); // "^-- " + assertEquals(true, set.contains('#')); + assertEquals(true, set.contains('^')); + assertEquals(true, set.contains('a')); + assertEquals(true, set.contains('*')); + assertEquals(true, set.contains('A')); + + set = CharSet.getInstance("^-b"); + array = set.getCharRanges(); + assertEquals(true, ArrayUtils.contains(array, CharRange.isIn('^','b'))); // "^-b" + assertEquals(true, set.contains('b')); + assertEquals(true, set.contains('_')); // between ^ and a + assertEquals(false, set.contains('A')); + assertEquals(true, set.contains('^')); + + set = CharSet.getInstance("b-^"); + array = set.getCharRanges(); + assertEquals(true, ArrayUtils.contains(array, CharRange.isIn('^','b'))); // "b-^" + assertEquals(true, set.contains('b')); + assertEquals(true, set.contains('^')); + assertEquals(true, set.contains('a')); // between ^ and b + assertEquals(false, set.contains('c')); + } + + //----------------------------------------------------------------------- + public void testEquals_Object() { + CharSet abc = CharSet.getInstance("abc"); + CharSet abc2 = CharSet.getInstance("abc"); + CharSet atoc = CharSet.getInstance("a-c"); + CharSet atoc2 = CharSet.getInstance("a-c"); + CharSet notatoc = CharSet.getInstance("^a-c"); + CharSet notatoc2 = CharSet.getInstance("^a-c"); + + assertEquals(false, abc.equals(null)); + + assertEquals(true, abc.equals(abc)); + assertEquals(true, abc.equals(abc2)); + assertEquals(false, abc.equals(atoc)); + assertEquals(false, abc.equals(notatoc)); + + assertEquals(false, atoc.equals(abc)); + assertEquals(true, atoc.equals(atoc)); + assertEquals(true, atoc.equals(atoc2)); + assertEquals(false, atoc.equals(notatoc)); + + assertEquals(false, notatoc.equals(abc)); + assertEquals(false, notatoc.equals(atoc)); + assertEquals(true, notatoc.equals(notatoc)); + assertEquals(true, notatoc.equals(notatoc2)); + } + + public void testHashCode() { + CharSet abc = CharSet.getInstance("abc"); + CharSet abc2 = CharSet.getInstance("abc"); + CharSet atoc = CharSet.getInstance("a-c"); + CharSet atoc2 = CharSet.getInstance("a-c"); + CharSet notatoc = CharSet.getInstance("^a-c"); + CharSet notatoc2 = CharSet.getInstance("^a-c"); + + assertEquals(abc.hashCode(), abc.hashCode()); + assertEquals(abc.hashCode(), abc2.hashCode()); + assertEquals(atoc.hashCode(), atoc.hashCode()); + assertEquals(atoc.hashCode(), atoc2.hashCode()); + assertEquals(notatoc.hashCode(), notatoc.hashCode()); + assertEquals(notatoc.hashCode(), notatoc2.hashCode()); + } + + //----------------------------------------------------------------------- + public void testContains_Char() { + CharSet btod = CharSet.getInstance("b-d"); + CharSet dtob = CharSet.getInstance("d-b"); + CharSet bcd = CharSet.getInstance("bcd"); + CharSet bd = CharSet.getInstance("bd"); + CharSet notbtod = CharSet.getInstance("^b-d"); + + assertEquals(false, btod.contains('a')); + assertEquals(true, btod.contains('b')); + assertEquals(true, btod.contains('c')); + assertEquals(true, btod.contains('d')); + assertEquals(false, btod.contains('e')); + + assertEquals(false, bcd.contains('a')); + assertEquals(true, bcd.contains('b')); + assertEquals(true, bcd.contains('c')); + assertEquals(true, bcd.contains('d')); + assertEquals(false, bcd.contains('e')); + + assertEquals(false, bd.contains('a')); + assertEquals(true, bd.contains('b')); + assertEquals(false, bd.contains('c')); + assertEquals(true, bd.contains('d')); + assertEquals(false, bd.contains('e')); + + assertEquals(true, notbtod.contains('a')); + assertEquals(false, notbtod.contains('b')); + assertEquals(false, notbtod.contains('c')); + assertEquals(false, notbtod.contains('d')); + assertEquals(true, notbtod.contains('e')); + + assertEquals(false, dtob.contains('a')); + assertEquals(true, dtob.contains('b')); + assertEquals(true, dtob.contains('c')); + assertEquals(true, dtob.contains('d')); + assertEquals(false, dtob.contains('e')); + + CharRange[] array = dtob.getCharRanges(); + assertEquals("[b-d]", dtob.toString()); + assertEquals(1, array.length); + } + + //----------------------------------------------------------------------- + public void testSerialization() { + CharSet set = CharSet.getInstance("a"); + assertEquals(set, SerializationUtils.clone(set)); + set = CharSet.getInstance("a-e"); + assertEquals(set, SerializationUtils.clone(set)); + set = CharSet.getInstance("be-f^a-z"); + assertEquals(set, SerializationUtils.clone(set)); + } + + //----------------------------------------------------------------------- + public void testStatics() { + CharRange[] array; + + array = CharSet.EMPTY.getCharRanges(); + assertEquals(0, array.length); + + array = CharSet.ASCII_ALPHA.getCharRanges(); + assertEquals(2, array.length); + assertEquals(true, ArrayUtils.contains(array, CharRange.isIn('a', 'z'))); + assertEquals(true, ArrayUtils.contains(array, CharRange.isIn('A', 'Z'))); + + array = CharSet.ASCII_ALPHA_LOWER.getCharRanges(); + assertEquals(1, array.length); + assertEquals(true, ArrayUtils.contains(array, CharRange.isIn('a', 'z'))); + + array = CharSet.ASCII_ALPHA_UPPER.getCharRanges(); + assertEquals(1, array.length); + assertEquals(true, ArrayUtils.contains(array, CharRange.isIn('A', 'Z'))); + + array = CharSet.ASCII_NUMERIC.getCharRanges(); + assertEquals(1, array.length); + assertEquals(true, ArrayUtils.contains(array, CharRange.isIn('0', '9'))); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/CharSetUtils.java b/ApacheCommonsLang/org/apache/commons/lang3/CharSetUtils.java new file mode 100644 index 0000000..2b66f9d --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/CharSetUtils.java @@ -0,0 +1,217 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3; + +/** + *

Operations on {@code CharSet} instances.

+ * + *

This class handles {@code null} input gracefully. + * An exception will not be thrown for a {@code null} input. + * Each method documents its behaviour in more detail.

+ * + *

#ThreadSafe#

+ * @see CharSet + * @since 1.0 + * @version $Id: CharSetUtils.java 1299411 2012-03-11 17:55:29Z ggregory $ + */ +public class CharSetUtils { + + /** + *

CharSetUtils instances should NOT be constructed in standard programming. + * Instead, the class should be used as {@code CharSetUtils.evaluateSet(null);}.

+ * + *

This constructor is public to permit tools that require a JavaBean instance + * to operate.

+ */ + public CharSetUtils() { + super(); + } + + // Squeeze + //----------------------------------------------------------------------- + /** + *

Squeezes any repetitions of a character that is mentioned in the + * supplied set.

+ * + *
+     * CharSetUtils.squeeze(null, *)        = null
+     * CharSetUtils.squeeze("", *)          = ""
+     * CharSetUtils.squeeze(*, null)        = *
+     * CharSetUtils.squeeze(*, "")          = *
+     * CharSetUtils.squeeze("hello", "k-p") = "helo"
+     * CharSetUtils.squeeze("hello", "a-e") = "hello"
+     * 
+ * + * @see CharSet#getInstance(java.lang.String...) for set-syntax. + * @param str the string to squeeze, may be null + * @param set the character set to use for manipulation, may be null + * @return the modified String, {@code null} if null string input + */ + public static String squeeze(String str, String... set) { + if (StringUtils.isEmpty(str) || deepEmpty(set)) { + return str; + } + CharSet chars = CharSet.getInstance(set); + StringBuilder buffer = new StringBuilder(str.length()); + char[] chrs = str.toCharArray(); + int sz = chrs.length; + char lastChar = ' '; + char ch = ' '; + for (int i = 0; i < sz; i++) { + ch = chrs[i]; + // Compare with contains() last for performance. + if (ch == lastChar && i != 0 && chars.contains(ch)) { + continue; + } + buffer.append(ch); + lastChar = ch; + } + return buffer.toString(); + } + + // Count + //----------------------------------------------------------------------- + /** + *

Takes an argument in set-syntax, see evaluateSet, + * and returns the number of characters present in the specified string.

+ * + *
+     * CharSetUtils.count(null, *)        = 0
+     * CharSetUtils.count("", *)          = 0
+     * CharSetUtils.count(*, null)        = 0
+     * CharSetUtils.count(*, "")          = 0
+     * CharSetUtils.count("hello", "k-p") = 3
+     * CharSetUtils.count("hello", "a-e") = 1
+     * 
+ * + * @see CharSet#getInstance(java.lang.String...) for set-syntax. + * @param str String to count characters in, may be null + * @param set String[] set of characters to count, may be null + * @return the character count, zero if null string input + */ + public static int count(String str, String... set) { + if (StringUtils.isEmpty(str) || deepEmpty(set)) { + return 0; + } + CharSet chars = CharSet.getInstance(set); + int count = 0; + for (char c : str.toCharArray()) { + if (chars.contains(c)) { + count++; + } + } + return count; + } + + // Keep + //----------------------------------------------------------------------- + /** + *

Takes an argument in set-syntax, see evaluateSet, + * and keeps any of characters present in the specified string.

+ * + *
+     * CharSetUtils.keep(null, *)        = null
+     * CharSetUtils.keep("", *)          = ""
+     * CharSetUtils.keep(*, null)        = ""
+     * CharSetUtils.keep(*, "")          = ""
+     * CharSetUtils.keep("hello", "hl")  = "hll"
+     * CharSetUtils.keep("hello", "le")  = "ell"
+     * 
+ * + * @see CharSet#getInstance(java.lang.String...) for set-syntax. + * @param str String to keep characters from, may be null + * @param set String[] set of characters to keep, may be null + * @return the modified String, {@code null} if null string input + * @since 2.0 + */ + public static String keep(String str, String... set) { + if (str == null) { + return null; + } + if (str.length() == 0 || deepEmpty(set)) { + return StringUtils.EMPTY; + } + return modify(str, set, true); + } + + // Delete + //----------------------------------------------------------------------- + /** + *

Takes an argument in set-syntax, see evaluateSet, + * and deletes any of characters present in the specified string.

+ * + *
+     * CharSetUtils.delete(null, *)        = null
+     * CharSetUtils.delete("", *)          = ""
+     * CharSetUtils.delete(*, null)        = *
+     * CharSetUtils.delete(*, "")          = *
+     * CharSetUtils.delete("hello", "hl")  = "eo"
+     * CharSetUtils.delete("hello", "le")  = "ho"
+     * 
+ * + * @see CharSet#getInstance(java.lang.String...) for set-syntax. + * @param str String to delete characters from, may be null + * @param set String[] set of characters to delete, may be null + * @return the modified String, {@code null} if null string input + */ + public static String delete(String str, String... set) { + if (StringUtils.isEmpty(str) || deepEmpty(set)) { + return str; + } + return modify(str, set, false); + } + + //----------------------------------------------------------------------- + /** + * Implementation of delete and keep + * + * @param str String to modify characters within + * @param set String[] set of characters to modify + * @param expect whether to evaluate on match, or non-match + * @return the modified String, not null + */ + private static String modify(String str, String[] set, boolean expect) { + CharSet chars = CharSet.getInstance(set); + StringBuilder buffer = new StringBuilder(str.length()); + char[] chrs = str.toCharArray(); + int sz = chrs.length; + for(int i=0; i[] cons = CharSetUtils.class.getDeclaredConstructors(); + assertEquals(1, cons.length); + assertEquals(true, Modifier.isPublic(cons[0].getModifiers())); + assertEquals(true, Modifier.isPublic(CharSetUtils.class.getModifiers())); + assertEquals(false, Modifier.isFinal(CharSetUtils.class.getModifiers())); + } + + //----------------------------------------------------------------------- + public void testSqueeze_StringString() { + assertEquals(null, CharSetUtils.squeeze(null, (String) null)); + assertEquals(null, CharSetUtils.squeeze(null, "")); + + assertEquals("", CharSetUtils.squeeze("", (String) null)); + assertEquals("", CharSetUtils.squeeze("", "")); + assertEquals("", CharSetUtils.squeeze("", "a-e")); + + assertEquals("hello", CharSetUtils.squeeze("hello", (String) null)); + assertEquals("hello", CharSetUtils.squeeze("hello", "")); + assertEquals("hello", CharSetUtils.squeeze("hello", "a-e")); + assertEquals("helo", CharSetUtils.squeeze("hello", "l-p")); + assertEquals("heloo", CharSetUtils.squeeze("helloo", "l")); + assertEquals("hello", CharSetUtils.squeeze("helloo", "^l")); + } + + public void testSqueeze_StringStringarray() { + assertEquals(null, CharSetUtils.squeeze(null, (String[]) null)); + assertEquals(null, CharSetUtils.squeeze(null, new String[0])); + assertEquals(null, CharSetUtils.squeeze(null, new String[] {null})); + assertEquals(null, CharSetUtils.squeeze(null, new String[] {"el"})); + + assertEquals("", CharSetUtils.squeeze("", (String[]) null)); + assertEquals("", CharSetUtils.squeeze("", new String[0])); + assertEquals("", CharSetUtils.squeeze("", new String[] {null})); + assertEquals("", CharSetUtils.squeeze("", new String[] {"a-e"})); + + assertEquals("hello", CharSetUtils.squeeze("hello", (String[]) null)); + assertEquals("hello", CharSetUtils.squeeze("hello", new String[0])); + assertEquals("hello", CharSetUtils.squeeze("hello", new String[] {null})); + assertEquals("hello", CharSetUtils.squeeze("hello", new String[] {"a-e"})); + + assertEquals("helo", CharSetUtils.squeeze("hello", new String[] { "el" })); + assertEquals("hello", CharSetUtils.squeeze("hello", new String[] { "e" })); + assertEquals("fofof", CharSetUtils.squeeze("fooffooff", new String[] { "of" })); + assertEquals("fof", CharSetUtils.squeeze("fooooff", new String[] { "fo" })); + } + + //----------------------------------------------------------------------- + public void testCount_StringString() { + assertEquals(0, CharSetUtils.count(null, (String) null)); + assertEquals(0, CharSetUtils.count(null, "")); + + assertEquals(0, CharSetUtils.count("", (String) null)); + assertEquals(0, CharSetUtils.count("", "")); + assertEquals(0, CharSetUtils.count("", "a-e")); + + assertEquals(0, CharSetUtils.count("hello", (String) null)); + assertEquals(0, CharSetUtils.count("hello", "")); + assertEquals(1, CharSetUtils.count("hello", "a-e")); + assertEquals(3, CharSetUtils.count("hello", "l-p")); + } + + public void testCount_StringStringarray() { + assertEquals(0, CharSetUtils.count(null, (String[]) null)); + assertEquals(0, CharSetUtils.count(null, new String[0])); + assertEquals(0, CharSetUtils.count(null, new String[] {null})); + assertEquals(0, CharSetUtils.count(null, new String[] {"a-e"})); + + assertEquals(0, CharSetUtils.count("", (String[]) null)); + assertEquals(0, CharSetUtils.count("", new String[0])); + assertEquals(0, CharSetUtils.count("", new String[] {null})); + assertEquals(0, CharSetUtils.count("", new String[] {"a-e"})); + + assertEquals(0, CharSetUtils.count("hello", (String[]) null)); + assertEquals(0, CharSetUtils.count("hello", new String[0])); + assertEquals(0, CharSetUtils.count("hello", new String[] {null})); + assertEquals(1, CharSetUtils.count("hello", new String[] {"a-e"})); + + assertEquals(3, CharSetUtils.count("hello", new String[] { "el" })); + assertEquals(0, CharSetUtils.count("hello", new String[] { "x" })); + assertEquals(2, CharSetUtils.count("hello", new String[] { "e-i" })); + assertEquals(5, CharSetUtils.count("hello", new String[] { "a-z" })); + assertEquals(0, CharSetUtils.count("hello", new String[] { "" })); + } + + //----------------------------------------------------------------------- + public void testKeep_StringString() { + assertEquals(null, CharSetUtils.keep(null, (String) null)); + assertEquals(null, CharSetUtils.keep(null, "")); + + assertEquals("", CharSetUtils.keep("", (String) null)); + assertEquals("", CharSetUtils.keep("", "")); + assertEquals("", CharSetUtils.keep("", "a-e")); + + assertEquals("", CharSetUtils.keep("hello", (String) null)); + assertEquals("", CharSetUtils.keep("hello", "")); + assertEquals("", CharSetUtils.keep("hello", "xyz")); + assertEquals("hello", CharSetUtils.keep("hello", "a-z")); + assertEquals("hello", CharSetUtils.keep("hello", "oleh")); + assertEquals("ell", CharSetUtils.keep("hello", "el")); + } + + public void testKeep_StringStringarray() { + assertEquals(null, CharSetUtils.keep(null, (String[]) null)); + assertEquals(null, CharSetUtils.keep(null, new String[0])); + assertEquals(null, CharSetUtils.keep(null, new String[] {null})); + assertEquals(null, CharSetUtils.keep(null, new String[] {"a-e"})); + + assertEquals("", CharSetUtils.keep("", (String[]) null)); + assertEquals("", CharSetUtils.keep("", new String[0])); + assertEquals("", CharSetUtils.keep("", new String[] {null})); + assertEquals("", CharSetUtils.keep("", new String[] {"a-e"})); + + assertEquals("", CharSetUtils.keep("hello", (String[]) null)); + assertEquals("", CharSetUtils.keep("hello", new String[0])); + assertEquals("", CharSetUtils.keep("hello", new String[] {null})); + assertEquals("e", CharSetUtils.keep("hello", new String[] {"a-e"})); + + assertEquals("e", CharSetUtils.keep("hello", new String[] { "a-e" })); + assertEquals("ell", CharSetUtils.keep("hello", new String[] { "el" })); + assertEquals("hello", CharSetUtils.keep("hello", new String[] { "elho" })); + assertEquals("hello", CharSetUtils.keep("hello", new String[] { "a-z" })); + assertEquals("----", CharSetUtils.keep("----", new String[] { "-" })); + assertEquals("ll", CharSetUtils.keep("hello", new String[] { "l" })); + } + + //----------------------------------------------------------------------- + public void testDelete_StringString() { + assertEquals(null, CharSetUtils.delete(null, (String) null)); + assertEquals(null, CharSetUtils.delete(null, "")); + + assertEquals("", CharSetUtils.delete("", (String) null)); + assertEquals("", CharSetUtils.delete("", "")); + assertEquals("", CharSetUtils.delete("", "a-e")); + + assertEquals("hello", CharSetUtils.delete("hello", (String) null)); + assertEquals("hello", CharSetUtils.delete("hello", "")); + assertEquals("hllo", CharSetUtils.delete("hello", "a-e")); + assertEquals("he", CharSetUtils.delete("hello", "l-p")); + assertEquals("hello", CharSetUtils.delete("hello", "z")); + } + + public void testDelete_StringStringarray() { + assertEquals(null, CharSetUtils.delete(null, (String[]) null)); + assertEquals(null, CharSetUtils.delete(null, new String[0])); + assertEquals(null, CharSetUtils.delete(null, new String[] {null})); + assertEquals(null, CharSetUtils.delete(null, new String[] {"el"})); + + assertEquals("", CharSetUtils.delete("", (String[]) null)); + assertEquals("", CharSetUtils.delete("", new String[0])); + assertEquals("", CharSetUtils.delete("", new String[] {null})); + assertEquals("", CharSetUtils.delete("", new String[] {"a-e"})); + + assertEquals("hello", CharSetUtils.delete("hello", (String[]) null)); + assertEquals("hello", CharSetUtils.delete("hello", new String[0])); + assertEquals("hello", CharSetUtils.delete("hello", new String[] {null})); + assertEquals("hello", CharSetUtils.delete("hello", new String[] {"xyz"})); + + assertEquals("ho", CharSetUtils.delete("hello", new String[] { "el" })); + assertEquals("", CharSetUtils.delete("hello", new String[] { "elho" })); + assertEquals("hello", CharSetUtils.delete("hello", new String[] { "" })); + assertEquals("hello", CharSetUtils.delete("hello", "")); + assertEquals("", CharSetUtils.delete("hello", new String[] { "a-z" })); + assertEquals("", CharSetUtils.delete("----", new String[] { "-" })); + assertEquals("heo", CharSetUtils.delete("hello", new String[] { "l" })); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/CharUtils.java b/ApacheCommonsLang/org/apache/commons/lang3/CharUtils.java new file mode 100644 index 0000000..7ce2d60 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/CharUtils.java @@ -0,0 +1,539 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3; + +/** + *

Operations on char primitives and Character objects.

+ * + *

This class tries to handle {@code null} input gracefully. + * An exception will not be thrown for a {@code null} input. + * Each method documents its behaviour in more detail.

+ * + *

#ThreadSafe#

+ * @since 2.1 + * @version $Id: CharUtils.java 1158279 2011-08-16 14:06:45Z ggregory $ + */ +public class CharUtils { + + private static final String[] CHAR_STRING_ARRAY = new String[128]; + + /** + * {@code \u000a} linefeed LF ('\n'). + * + * @see JLF: Escape Sequences + * for Character and String Literals + * @since 2.2 + */ + public static final char LF = '\n'; + + /** + * {@code \u000d} carriage return CR ('\r'). + * + * @see JLF: Escape Sequences + * for Character and String Literals + * @since 2.2 + */ + public static final char CR = '\r'; + + + static { + for (char c = 0; c < CHAR_STRING_ARRAY.length; c++) { + CHAR_STRING_ARRAY[c] = String.valueOf(c); + } + } + + /** + *

{@code CharUtils} instances should NOT be constructed in standard programming. + * Instead, the class should be used as {@code CharUtils.toString('c');}.

+ * + *

This constructor is public to permit tools that require a JavaBean instance + * to operate.

+ */ + public CharUtils() { + super(); + } + + //----------------------------------------------------------------------- + /** + *

Converts the character to a Character.

+ * + *

For ASCII 7 bit characters, this uses a cache that will return the + * same Character object each time.

+ * + *
+     *   CharUtils.toCharacterObject(' ')  = ' '
+     *   CharUtils.toCharacterObject('A')  = 'A'
+     * 
+ * + * @deprecated Java 5 introduced {@link Character#valueOf(char)} which caches chars 0 through 127. + * @param ch the character to convert + * @return a Character of the specified character + */ + @Deprecated + public static Character toCharacterObject(char ch) { + return Character.valueOf(ch); + } + + /** + *

Converts the String to a Character using the first character, returning + * null for empty Strings.

+ * + *

For ASCII 7 bit characters, this uses a cache that will return the + * same Character object each time.

+ * + *
+     *   CharUtils.toCharacterObject(null) = null
+     *   CharUtils.toCharacterObject("")   = null
+     *   CharUtils.toCharacterObject("A")  = 'A'
+     *   CharUtils.toCharacterObject("BA") = 'B'
+     * 
+ * + * @param str the character to convert + * @return the Character value of the first letter of the String + */ + public static Character toCharacterObject(String str) { + if (StringUtils.isEmpty(str)) { + return null; + } + return Character.valueOf(str.charAt(0)); + } + + //----------------------------------------------------------------------- + /** + *

Converts the Character to a char throwing an exception for {@code null}.

+ * + *
+     *   CharUtils.toChar(' ')  = ' '
+     *   CharUtils.toChar('A')  = 'A'
+     *   CharUtils.toChar(null) throws IllegalArgumentException
+     * 
+ * + * @param ch the character to convert + * @return the char value of the Character + * @throws IllegalArgumentException if the Character is null + */ + public static char toChar(Character ch) { + if (ch == null) { + throw new IllegalArgumentException("The Character must not be null"); + } + return ch.charValue(); + } + + /** + *

Converts the Character to a char handling {@code null}.

+ * + *
+     *   CharUtils.toChar(null, 'X') = 'X'
+     *   CharUtils.toChar(' ', 'X')  = ' '
+     *   CharUtils.toChar('A', 'X')  = 'A'
+     * 
+ * + * @param ch the character to convert + * @param defaultValue the value to use if the Character is null + * @return the char value of the Character or the default if null + */ + public static char toChar(Character ch, char defaultValue) { + if (ch == null) { + return defaultValue; + } + return ch.charValue(); + } + + //----------------------------------------------------------------------- + /** + *

Converts the String to a char using the first character, throwing + * an exception on empty Strings.

+ * + *
+     *   CharUtils.toChar("A")  = 'A'
+     *   CharUtils.toChar("BA") = 'B'
+     *   CharUtils.toChar(null) throws IllegalArgumentException
+     *   CharUtils.toChar("")   throws IllegalArgumentException
+     * 
+ * + * @param str the character to convert + * @return the char value of the first letter of the String + * @throws IllegalArgumentException if the String is empty + */ + public static char toChar(String str) { + if (StringUtils.isEmpty(str)) { + throw new IllegalArgumentException("The String must not be empty"); + } + return str.charAt(0); + } + + /** + *

Converts the String to a char using the first character, defaulting + * the value on empty Strings.

+ * + *
+     *   CharUtils.toChar(null, 'X') = 'X'
+     *   CharUtils.toChar("", 'X')   = 'X'
+     *   CharUtils.toChar("A", 'X')  = 'A'
+     *   CharUtils.toChar("BA", 'X') = 'B'
+     * 
+ * + * @param str the character to convert + * @param defaultValue the value to use if the Character is null + * @return the char value of the first letter of the String or the default if null + */ + public static char toChar(String str, char defaultValue) { + if (StringUtils.isEmpty(str)) { + return defaultValue; + } + return str.charAt(0); + } + + //----------------------------------------------------------------------- + /** + *

Converts the character to the Integer it represents, throwing an + * exception if the character is not numeric.

+ * + *

This method coverts the char '1' to the int 1 and so on.

+ * + *
+     *   CharUtils.toIntValue('3')  = 3
+     *   CharUtils.toIntValue('A')  throws IllegalArgumentException
+     * 
+ * + * @param ch the character to convert + * @return the int value of the character + * @throws IllegalArgumentException if the character is not ASCII numeric + */ + public static int toIntValue(char ch) { + if (isAsciiNumeric(ch) == false) { + throw new IllegalArgumentException("The character " + ch + " is not in the range '0' - '9'"); + } + return ch - 48; + } + + /** + *

Converts the character to the Integer it represents, throwing an + * exception if the character is not numeric.

+ * + *

This method coverts the char '1' to the int 1 and so on.

+ * + *
+     *   CharUtils.toIntValue('3', -1)  = 3
+     *   CharUtils.toIntValue('A', -1)  = -1
+     * 
+ * + * @param ch the character to convert + * @param defaultValue the default value to use if the character is not numeric + * @return the int value of the character + */ + public static int toIntValue(char ch, int defaultValue) { + if (isAsciiNumeric(ch) == false) { + return defaultValue; + } + return ch - 48; + } + + /** + *

Converts the character to the Integer it represents, throwing an + * exception if the character is not numeric.

+ * + *

This method coverts the char '1' to the int 1 and so on.

+ * + *
+     *   CharUtils.toIntValue('3')  = 3
+     *   CharUtils.toIntValue(null) throws IllegalArgumentException
+     *   CharUtils.toIntValue('A')  throws IllegalArgumentException
+     * 
+ * + * @param ch the character to convert, not null + * @return the int value of the character + * @throws IllegalArgumentException if the Character is not ASCII numeric or is null + */ + public static int toIntValue(Character ch) { + if (ch == null) { + throw new IllegalArgumentException("The character must not be null"); + } + return toIntValue(ch.charValue()); + } + + /** + *

Converts the character to the Integer it represents, throwing an + * exception if the character is not numeric.

+ * + *

This method coverts the char '1' to the int 1 and so on.

+ * + *
+     *   CharUtils.toIntValue(null, -1) = -1
+     *   CharUtils.toIntValue('3', -1)  = 3
+     *   CharUtils.toIntValue('A', -1)  = -1
+     * 
+ * + * @param ch the character to convert + * @param defaultValue the default value to use if the character is not numeric + * @return the int value of the character + */ + public static int toIntValue(Character ch, int defaultValue) { + if (ch == null) { + return defaultValue; + } + return toIntValue(ch.charValue(), defaultValue); + } + + //----------------------------------------------------------------------- + /** + *

Converts the character to a String that contains the one character.

+ * + *

For ASCII 7 bit characters, this uses a cache that will return the + * same String object each time.

+ * + *
+     *   CharUtils.toString(' ')  = " "
+     *   CharUtils.toString('A')  = "A"
+     * 
+ * + * @param ch the character to convert + * @return a String containing the one specified character + */ + public static String toString(char ch) { + if (ch < 128) { + return CHAR_STRING_ARRAY[ch]; + } + return new String(new char[] {ch}); + } + + /** + *

Converts the character to a String that contains the one character.

+ * + *

For ASCII 7 bit characters, this uses a cache that will return the + * same String object each time.

+ * + *

If {@code null} is passed in, {@code null} will be returned.

+ * + *
+     *   CharUtils.toString(null) = null
+     *   CharUtils.toString(' ')  = " "
+     *   CharUtils.toString('A')  = "A"
+     * 
+ * + * @param ch the character to convert + * @return a String containing the one specified character + */ + public static String toString(Character ch) { + if (ch == null) { + return null; + } + return toString(ch.charValue()); + } + + //-------------------------------------------------------------------------- + /** + *

Converts the string to the Unicode format '\u0020'.

+ * + *

This format is the Java source code format.

+ * + *
+     *   CharUtils.unicodeEscaped(' ') = "\u0020"
+     *   CharUtils.unicodeEscaped('A') = "\u0041"
+     * 
+ * + * @param ch the character to convert + * @return the escaped Unicode string + */ + public static String unicodeEscaped(char ch) { + if (ch < 0x10) { + return "\\u000" + Integer.toHexString(ch); + } else if (ch < 0x100) { + return "\\u00" + Integer.toHexString(ch); + } else if (ch < 0x1000) { + return "\\u0" + Integer.toHexString(ch); + } + return "\\u" + Integer.toHexString(ch); + } + + /** + *

Converts the string to the Unicode format '\u0020'.

+ * + *

This format is the Java source code format.

+ * + *

If {@code null} is passed in, {@code null} will be returned.

+ * + *
+     *   CharUtils.unicodeEscaped(null) = null
+     *   CharUtils.unicodeEscaped(' ')  = "\u0020"
+     *   CharUtils.unicodeEscaped('A')  = "\u0041"
+     * 
+ * + * @param ch the character to convert, may be null + * @return the escaped Unicode string, null if null input + */ + public static String unicodeEscaped(Character ch) { + if (ch == null) { + return null; + } + return unicodeEscaped(ch.charValue()); + } + + //-------------------------------------------------------------------------- + /** + *

Checks whether the character is ASCII 7 bit.

+ * + *
+     *   CharUtils.isAscii('a')  = true
+     *   CharUtils.isAscii('A')  = true
+     *   CharUtils.isAscii('3')  = true
+     *   CharUtils.isAscii('-')  = true
+     *   CharUtils.isAscii('\n') = true
+     *   CharUtils.isAscii('©') = false
+     * 
+ * + * @param ch the character to check + * @return true if less than 128 + */ + public static boolean isAscii(char ch) { + return ch < 128; + } + + /** + *

Checks whether the character is ASCII 7 bit printable.

+ * + *
+     *   CharUtils.isAsciiPrintable('a')  = true
+     *   CharUtils.isAsciiPrintable('A')  = true
+     *   CharUtils.isAsciiPrintable('3')  = true
+     *   CharUtils.isAsciiPrintable('-')  = true
+     *   CharUtils.isAsciiPrintable('\n') = false
+     *   CharUtils.isAsciiPrintable('©') = false
+     * 
+ * + * @param ch the character to check + * @return true if between 32 and 126 inclusive + */ + public static boolean isAsciiPrintable(char ch) { + return ch >= 32 && ch < 127; + } + + /** + *

Checks whether the character is ASCII 7 bit control.

+ * + *
+     *   CharUtils.isAsciiControl('a')  = false
+     *   CharUtils.isAsciiControl('A')  = false
+     *   CharUtils.isAsciiControl('3')  = false
+     *   CharUtils.isAsciiControl('-')  = false
+     *   CharUtils.isAsciiControl('\n') = true
+     *   CharUtils.isAsciiControl('©') = false
+     * 
+ * + * @param ch the character to check + * @return true if less than 32 or equals 127 + */ + public static boolean isAsciiControl(char ch) { + return ch < 32 || ch == 127; + } + + /** + *

Checks whether the character is ASCII 7 bit alphabetic.

+ * + *
+     *   CharUtils.isAsciiAlpha('a')  = true
+     *   CharUtils.isAsciiAlpha('A')  = true
+     *   CharUtils.isAsciiAlpha('3')  = false
+     *   CharUtils.isAsciiAlpha('-')  = false
+     *   CharUtils.isAsciiAlpha('\n') = false
+     *   CharUtils.isAsciiAlpha('©') = false
+     * 
+ * + * @param ch the character to check + * @return true if between 65 and 90 or 97 and 122 inclusive + */ + public static boolean isAsciiAlpha(char ch) { + return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z'); + } + + /** + *

Checks whether the character is ASCII 7 bit alphabetic upper case.

+ * + *
+     *   CharUtils.isAsciiAlphaUpper('a')  = false
+     *   CharUtils.isAsciiAlphaUpper('A')  = true
+     *   CharUtils.isAsciiAlphaUpper('3')  = false
+     *   CharUtils.isAsciiAlphaUpper('-')  = false
+     *   CharUtils.isAsciiAlphaUpper('\n') = false
+     *   CharUtils.isAsciiAlphaUpper('©') = false
+     * 
+ * + * @param ch the character to check + * @return true if between 65 and 90 inclusive + */ + public static boolean isAsciiAlphaUpper(char ch) { + return ch >= 'A' && ch <= 'Z'; + } + + /** + *

Checks whether the character is ASCII 7 bit alphabetic lower case.

+ * + *
+     *   CharUtils.isAsciiAlphaLower('a')  = true
+     *   CharUtils.isAsciiAlphaLower('A')  = false
+     *   CharUtils.isAsciiAlphaLower('3')  = false
+     *   CharUtils.isAsciiAlphaLower('-')  = false
+     *   CharUtils.isAsciiAlphaLower('\n') = false
+     *   CharUtils.isAsciiAlphaLower('©') = false
+     * 
+ * + * @param ch the character to check + * @return true if between 97 and 122 inclusive + */ + public static boolean isAsciiAlphaLower(char ch) { + return ch >= 'a' && ch <= 'z'; + } + + /** + *

Checks whether the character is ASCII 7 bit numeric.

+ * + *
+     *   CharUtils.isAsciiNumeric('a')  = false
+     *   CharUtils.isAsciiNumeric('A')  = false
+     *   CharUtils.isAsciiNumeric('3')  = true
+     *   CharUtils.isAsciiNumeric('-')  = false
+     *   CharUtils.isAsciiNumeric('\n') = false
+     *   CharUtils.isAsciiNumeric('©') = false
+     * 
+ * + * @param ch the character to check + * @return true if between 48 and 57 inclusive + */ + public static boolean isAsciiNumeric(char ch) { + return ch >= '0' && ch <= '9'; + } + + /** + *

Checks whether the character is ASCII 7 bit numeric.

+ * + *
+     *   CharUtils.isAsciiAlphanumeric('a')  = true
+     *   CharUtils.isAsciiAlphanumeric('A')  = true
+     *   CharUtils.isAsciiAlphanumeric('3')  = true
+     *   CharUtils.isAsciiAlphanumeric('-')  = false
+     *   CharUtils.isAsciiAlphanumeric('\n') = false
+     *   CharUtils.isAsciiAlphanumeric('©') = false
+     * 
+ * + * @param ch the character to check + * @return true if between 48 and 57 or 65 and 90 or 97 and 122 inclusive + */ + public static boolean isAsciiAlphanumeric(char ch) { + return (ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9'); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/CharUtilsPerfRun.java b/ApacheCommonsLang/org/apache/commons/lang3/CharUtilsPerfRun.java new file mode 100644 index 0000000..00f1232 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/CharUtilsPerfRun.java @@ -0,0 +1,162 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.lang3; + +import java.text.NumberFormat; +import java.util.Calendar; + +/** + * Tests the difference in performance between CharUtils and CharSet. + * + * Sample runs: + +Now: Thu Mar 18 14:29:48 PST 2004 +Sun Microsystems Inc. Java(TM) 2 Runtime Environment, Standard Edition 1.3.1_10-b03 +Sun Microsystems Inc. Java HotSpot(TM) Client VM 1.3.1_10-b03 +Windows XP 5.1 x86 pentium i486 i386 +Do nohting: 0 milliseconds. +run_CharUtils_isAsciiNumeric: 4,545 milliseconds. +run_inlined_CharUtils_isAsciiNumeric: 3,417 milliseconds. +run_inlined_CharUtils_isAsciiNumeric: 85,679 milliseconds. + + +Now: Thu Mar 18 14:24:51 PST 2004 +Sun Microsystems Inc. Java(TM) 2 Runtime Environment, Standard Edition 1.4.2_04-b05 +Sun Microsystems Inc. Java HotSpot(TM) Client VM 1.4.2_04-b05 +Windows XP 5.1 x86 pentium i486 i386 +Do nohting: 0 milliseconds. +run_CharUtils_isAsciiNumeric: 2,578 milliseconds. +run_inlined_CharUtils_isAsciiNumeric: 2,477 milliseconds. +run_inlined_CharUtils_isAsciiNumeric: 114,429 milliseconds. + +Now: Thu Mar 18 14:27:55 PST 2004 +Sun Microsystems Inc. Java(TM) 2 Runtime Environment, Standard Edition 1.4.2_04-b05 +Sun Microsystems Inc. Java HotSpot(TM) Server VM 1.4.2_04-b05 +Windows XP 5.1 x86 pentium i486 i386 +Do nohting: 0 milliseconds. +run_CharUtils_isAsciiNumeric: 630 milliseconds. +run_inlined_CharUtils_isAsciiNumeric: 709 milliseconds. +run_inlined_CharUtils_isAsciiNumeric: 84,420 milliseconds. + + + * @version $Id: CharUtilsPerfRun.java 1199894 2011-11-09 17:53:59Z ggregory $ + */ +public class CharUtilsPerfRun { + final static String VERSION = "$Id: CharUtilsPerfRun.java 1199894 2011-11-09 17:53:59Z ggregory $"; + + final static int WARM_UP = 100; + + final static int COUNT = 5000; + + final static char[] CHAR_SAMPLES; + static { + CHAR_SAMPLES = new char[Character.MAX_VALUE]; + for (char i = Character.MIN_VALUE; i < Character.MAX_VALUE; i++) { + CHAR_SAMPLES[i] = i; + } + } + + public static void main(String[] args) { + new CharUtilsPerfRun().run(); + } + + private void printSysInfo() { + System.out.println(VERSION); + System.out.println("Now: " + Calendar.getInstance().getTime()); + System.out.println(System.getProperty("java.vendor") + + " " + + System.getProperty("java.runtime.name") + + " " + + System.getProperty("java.runtime.version")); + System.out.println(System.getProperty("java.vm.vendor") + + " " + + System.getProperty("java.vm.name") + + " " + + System.getProperty("java.vm.version")); + System.out.println(System.getProperty("os.name") + + " " + + System.getProperty("os.version") + + " " + + System.getProperty("os.arch") + + " " + + System.getProperty("sun.cpu.isalist")); + } + + private void run() { + this.printSysInfo(); + long start; + start = System.currentTimeMillis(); + this.printlnTotal("Do nohting", start); + //System.out.println("Warming up..."); + run_CharUtils_isAsciiNumeric(WARM_UP); + //System.out.println("Measuring..."); + start = System.currentTimeMillis(); + run_CharUtils_isAsciiNumeric(COUNT); + this.printlnTotal("run_CharUtils_isAsciiNumeric", start); + //System.out.println("Warming up..."); + run_inlined_CharUtils_isAsciiNumeric(WARM_UP); + //System.out.println("Measuring..."); + start = System.currentTimeMillis(); + run_inlined_CharUtils_isAsciiNumeric(COUNT); + this.printlnTotal("run_inlined_CharUtils_isAsciiNumeric", start); + //System.out.println("Warming up..."); + run_CharSet(WARM_UP); + //System.out.println("Measuring..."); + start = System.currentTimeMillis(); + run_CharSet(COUNT); + this.printlnTotal("run_CharSet", start); + } + + private int run_CharSet(int loopCount) { + int t = 0; + for (int i = 0; i < loopCount; i++) { + for (char ch : CHAR_SAMPLES) { + boolean b = CharSet.ASCII_NUMERIC.contains(ch); + t += b ? 1 : 0; + } + } + return t; + } + + private int run_CharUtils_isAsciiNumeric(int loopCount) { + int t = 0; + for (int i = 0; i < loopCount; i++) { + for (char ch : CHAR_SAMPLES) { + boolean b = CharUtils.isAsciiNumeric(ch); + t += b ? 1 : 0; + } + } + return t; + } + + private int run_inlined_CharUtils_isAsciiNumeric(int loopCount) { + int t = 0; + for (int i = 0; i < loopCount; i++) { + for (char ch : CHAR_SAMPLES) { + boolean b = ch >= '0' && ch <= '9'; + t += b ? 1 : 0; + } + } + return t; + } + + private void printlnTotal(String prefix, long start) { + long total = System.currentTimeMillis() - start; + System.out.println(prefix + ": " + NumberFormat.getInstance().format(total) + " milliseconds."); + } +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/CharUtilsTest.java b/ApacheCommonsLang/org/apache/commons/lang3/CharUtilsTest.java new file mode 100644 index 0000000..3de18bf --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/CharUtilsTest.java @@ -0,0 +1,357 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Modifier; + +import org.junit.Test; + +/** + * Unit tests {@link org.apache.commons.lang3.CharUtils}. + * + * @version $Id: CharUtilsTest.java 1199724 2011-11-09 12:51:52Z sebb $ + */ +public class CharUtilsTest { + + private static final Character CHARACTER_A = new Character('A'); + private static final Character CHARACTER_B = new Character('B'); + private static final char CHAR_COPY = '\u00a9'; + + @Test + public void testConstructor() { + assertNotNull(new CharUtils()); + Constructor[] cons = CharUtils.class.getDeclaredConstructors(); + assertEquals(1, cons.length); + assertTrue(Modifier.isPublic(cons[0].getModifiers())); + assertTrue(Modifier.isPublic(BooleanUtils.class.getModifiers())); + assertFalse(Modifier.isFinal(BooleanUtils.class.getModifiers())); + } + + @Test + public void testToCharacterObject_char() { + assertEquals(new Character('a'), CharUtils.toCharacterObject('a')); + assertSame(CharUtils.toCharacterObject('a'), CharUtils.toCharacterObject('a')); + + for (int i = 0; i < 128; i++) { + Character ch = CharUtils.toCharacterObject((char) i); + Character ch2 = CharUtils.toCharacterObject((char) i); + assertSame(ch, ch2); + assertEquals(i, ch.charValue()); + } + for (int i = 128; i < 196; i++) { + Character ch = CharUtils.toCharacterObject((char) i); + Character ch2 = CharUtils.toCharacterObject((char) i); + assertEquals(ch, ch2); + assertTrue(ch != ch2); + assertEquals(i, ch.charValue()); + assertEquals(i, ch2.charValue()); + } + } + + @Test + public void testToCharacterObject_String() { + assertEquals(null, CharUtils.toCharacterObject(null)); + assertEquals(null, CharUtils.toCharacterObject("")); + assertEquals(new Character('a'), CharUtils.toCharacterObject("a")); + assertEquals(new Character('a'), CharUtils.toCharacterObject("abc")); + assertSame(CharUtils.toCharacterObject("a"), CharUtils.toCharacterObject("a")); + assertSame(CharUtils.toCharacterObject("a"), CharUtils.toCharacterObject('a')); + } + + @Test + public void testToChar_Character() { + assertEquals('A', CharUtils.toChar(CHARACTER_A)); + assertEquals('B', CharUtils.toChar(CHARACTER_B)); + try { + CharUtils.toChar((Character) null); + } catch (IllegalArgumentException ex) {} + } + + @Test + public void testToChar_Character_char() { + assertEquals('A', CharUtils.toChar(CHARACTER_A, 'X')); + assertEquals('B', CharUtils.toChar(CHARACTER_B, 'X')); + assertEquals('X', CharUtils.toChar((Character) null, 'X')); + } + + @Test + public void testToChar_String() { + assertEquals('A', CharUtils.toChar("A")); + assertEquals('B', CharUtils.toChar("BA")); + try { + CharUtils.toChar((String) null); + } catch (IllegalArgumentException ex) {} + try { + CharUtils.toChar(""); + } catch (IllegalArgumentException ex) {} + } + + @Test + public void testToChar_String_char() { + assertEquals('A', CharUtils.toChar("A", 'X')); + assertEquals('B', CharUtils.toChar("BA", 'X')); + assertEquals('X', CharUtils.toChar("", 'X')); + assertEquals('X', CharUtils.toChar((String) null, 'X')); + } + + @Test + public void testToIntValue_char() { + assertEquals(0, CharUtils.toIntValue('0')); + assertEquals(1, CharUtils.toIntValue('1')); + assertEquals(2, CharUtils.toIntValue('2')); + assertEquals(3, CharUtils.toIntValue('3')); + assertEquals(4, CharUtils.toIntValue('4')); + assertEquals(5, CharUtils.toIntValue('5')); + assertEquals(6, CharUtils.toIntValue('6')); + assertEquals(7, CharUtils.toIntValue('7')); + assertEquals(8, CharUtils.toIntValue('8')); + assertEquals(9, CharUtils.toIntValue('9')); + try { + CharUtils.toIntValue('a'); + } catch (IllegalArgumentException ex) {} + } + + @Test + public void testToIntValue_char_int() { + assertEquals(0, CharUtils.toIntValue('0', -1)); + assertEquals(3, CharUtils.toIntValue('3', -1)); + assertEquals(-1, CharUtils.toIntValue('a', -1)); + } + + @Test + public void testToIntValue_Character() { + assertEquals(0, CharUtils.toIntValue(new Character('0'))); + assertEquals(3, CharUtils.toIntValue(new Character('3'))); + try { + CharUtils.toIntValue(null); + } catch (IllegalArgumentException ex) {} + try { + CharUtils.toIntValue(CHARACTER_A); + } catch (IllegalArgumentException ex) {} + } + + @Test + public void testToIntValue_Character_int() { + assertEquals(0, CharUtils.toIntValue(new Character('0'), -1)); + assertEquals(3, CharUtils.toIntValue(new Character('3'), -1)); + assertEquals(-1, CharUtils.toIntValue(new Character('A'), -1)); + assertEquals(-1, CharUtils.toIntValue(null, -1)); + } + + @Test + public void testToString_char() { + assertEquals("a", CharUtils.toString('a')); + assertSame(CharUtils.toString('a'), CharUtils.toString('a')); + + for (int i = 0; i < 128; i++) { + String str = CharUtils.toString((char) i); + String str2 = CharUtils.toString((char) i); + assertSame(str, str2); + assertEquals(1, str.length()); + assertEquals(i, str.charAt(0)); + } + for (int i = 128; i < 196; i++) { + String str = CharUtils.toString((char) i); + String str2 = CharUtils.toString((char) i); + assertEquals(str, str2); + assertTrue(str != str2); + assertEquals(1, str.length()); + assertEquals(i, str.charAt(0)); + assertEquals(1, str2.length()); + assertEquals(i, str2.charAt(0)); + } + } + + @Test + public void testToString_Character() { + assertEquals(null, CharUtils.toString(null)); + assertEquals("A", CharUtils.toString(CHARACTER_A)); + assertSame(CharUtils.toString(CHARACTER_A), CharUtils.toString(CHARACTER_A)); + } + + @Test + public void testToUnicodeEscaped_char() { + assertEquals("\\u0041", CharUtils.unicodeEscaped('A')); + + for (int i = 0; i < 196; i++) { + String str = CharUtils.unicodeEscaped((char) i); + assertEquals(6, str.length()); + int val = Integer.parseInt(str.substring(2), 16); + assertEquals(i, val); + } + assertEquals("\\u0999", CharUtils.unicodeEscaped((char) 0x999)); + assertEquals("\\u1001", CharUtils.unicodeEscaped((char) 0x1001)); + } + + @Test + public void testToUnicodeEscaped_Character() { + assertEquals(null, CharUtils.unicodeEscaped(null)); + assertEquals("\\u0041", CharUtils.unicodeEscaped(CHARACTER_A)); + } + + @Test + public void testIsAscii_char() { + assertTrue(CharUtils.isAscii('a')); + assertTrue(CharUtils.isAscii('A')); + assertTrue(CharUtils.isAscii('3')); + assertTrue(CharUtils.isAscii('-')); + assertTrue(CharUtils.isAscii('\n')); + assertFalse(CharUtils.isAscii(CHAR_COPY)); + + for (int i = 0; i < 128; i++) { + if (i < 128) { + assertTrue(CharUtils.isAscii((char) i)); + } else { + assertFalse(CharUtils.isAscii((char) i)); + } + } + } + + @Test + public void testIsAsciiPrintable_char() { + assertTrue(CharUtils.isAsciiPrintable('a')); + assertTrue(CharUtils.isAsciiPrintable('A')); + assertTrue(CharUtils.isAsciiPrintable('3')); + assertTrue(CharUtils.isAsciiPrintable('-')); + assertFalse(CharUtils.isAsciiPrintable('\n')); + assertFalse(CharUtils.isAscii(CHAR_COPY)); + + for (int i = 0; i < 196; i++) { + if (i >= 32 && i <= 126) { + assertTrue(CharUtils.isAsciiPrintable((char) i)); + } else { + assertFalse(CharUtils.isAsciiPrintable((char) i)); + } + } + } + + @Test + public void testIsAsciiControl_char() { + assertFalse(CharUtils.isAsciiControl('a')); + assertFalse(CharUtils.isAsciiControl('A')); + assertFalse(CharUtils.isAsciiControl('3')); + assertFalse(CharUtils.isAsciiControl('-')); + assertTrue(CharUtils.isAsciiControl('\n')); + assertFalse(CharUtils.isAsciiControl(CHAR_COPY)); + + for (int i = 0; i < 196; i++) { + if (i < 32 || i == 127) { + assertTrue(CharUtils.isAsciiControl((char) i)); + } else { + assertFalse(CharUtils.isAsciiControl((char) i)); + } + } + } + + @Test + public void testIsAsciiAlpha_char() { + assertTrue(CharUtils.isAsciiAlpha('a')); + assertTrue(CharUtils.isAsciiAlpha('A')); + assertFalse(CharUtils.isAsciiAlpha('3')); + assertFalse(CharUtils.isAsciiAlpha('-')); + assertFalse(CharUtils.isAsciiAlpha('\n')); + assertFalse(CharUtils.isAsciiAlpha(CHAR_COPY)); + + for (int i = 0; i < 196; i++) { + if ((i >= 'A' && i <= 'Z') || (i >= 'a' && i <= 'z')) { + assertTrue(CharUtils.isAsciiAlpha((char) i)); + } else { + assertFalse(CharUtils.isAsciiAlpha((char) i)); + } + } + } + + @Test + public void testIsAsciiAlphaUpper_char() { + assertFalse(CharUtils.isAsciiAlphaUpper('a')); + assertTrue(CharUtils.isAsciiAlphaUpper('A')); + assertFalse(CharUtils.isAsciiAlphaUpper('3')); + assertFalse(CharUtils.isAsciiAlphaUpper('-')); + assertFalse(CharUtils.isAsciiAlphaUpper('\n')); + assertFalse(CharUtils.isAsciiAlphaUpper(CHAR_COPY)); + + for (int i = 0; i < 196; i++) { + if (i >= 'A' && i <= 'Z') { + assertTrue(CharUtils.isAsciiAlphaUpper((char) i)); + } else { + assertFalse(CharUtils.isAsciiAlphaUpper((char) i)); + } + } + } + + @Test + public void testIsAsciiAlphaLower_char() { + assertTrue(CharUtils.isAsciiAlphaLower('a')); + assertFalse(CharUtils.isAsciiAlphaLower('A')); + assertFalse(CharUtils.isAsciiAlphaLower('3')); + assertFalse(CharUtils.isAsciiAlphaLower('-')); + assertFalse(CharUtils.isAsciiAlphaLower('\n')); + assertFalse(CharUtils.isAsciiAlphaLower(CHAR_COPY)); + + for (int i = 0; i < 196; i++) { + if (i >= 'a' && i <= 'z') { + assertTrue(CharUtils.isAsciiAlphaLower((char) i)); + } else { + assertFalse(CharUtils.isAsciiAlphaLower((char) i)); + } + } + } + + @Test + public void testIsAsciiNumeric_char() { + assertFalse(CharUtils.isAsciiNumeric('a')); + assertFalse(CharUtils.isAsciiNumeric('A')); + assertTrue(CharUtils.isAsciiNumeric('3')); + assertFalse(CharUtils.isAsciiNumeric('-')); + assertFalse(CharUtils.isAsciiNumeric('\n')); + assertFalse(CharUtils.isAsciiNumeric(CHAR_COPY)); + + for (int i = 0; i < 196; i++) { + if (i >= '0' && i <= '9') { + assertTrue(CharUtils.isAsciiNumeric((char) i)); + } else { + assertFalse(CharUtils.isAsciiNumeric((char) i)); + } + } + } + + @Test + public void testIsAsciiAlphanumeric_char() { + assertTrue(CharUtils.isAsciiAlphanumeric('a')); + assertTrue(CharUtils.isAsciiAlphanumeric('A')); + assertTrue(CharUtils.isAsciiAlphanumeric('3')); + assertFalse(CharUtils.isAsciiAlphanumeric('-')); + assertFalse(CharUtils.isAsciiAlphanumeric('\n')); + assertFalse(CharUtils.isAsciiAlphanumeric(CHAR_COPY)); + + for (int i = 0; i < 196; i++) { + if ((i >= 'A' && i <= 'Z') || (i >= 'a' && i <= 'z') || (i >= '0' && i <= '9')) { + assertTrue(CharUtils.isAsciiAlphanumeric((char) i)); + } else { + assertFalse(CharUtils.isAsciiAlphanumeric((char) i)); + } + } + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/ClassUtils.java b/ApacheCommonsLang/org/apache/commons/lang3/ClassUtils.java new file mode 100644 index 0000000..184344c --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/ClassUtils.java @@ -0,0 +1,1132 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; + + +/** + *

Operates on classes without using reflection.

+ * + *

This class handles invalid {@code null} inputs as best it can. + * Each method documents its behaviour in more detail.

+ * + *

The notion of a {@code canonical name} includes the human + * readable name for the type, for example {@code int[]}. The + * non-canonical method variants work with the JVM names, such as + * {@code [I}.

+ * + * @since 2.0 + * @version $Id: ClassUtils.java 1299411 2012-03-11 17:55:29Z ggregory $ + */ +public class ClassUtils { + + /** + *

The package separator character: '.' == {@value}.

+ */ + public static final char PACKAGE_SEPARATOR_CHAR = '.'; + + /** + *

The package separator String: ".".

+ */ + public static final String PACKAGE_SEPARATOR = String.valueOf(PACKAGE_SEPARATOR_CHAR); + + /** + *

The inner class separator character: '$' == {@value}.

+ */ + public static final char INNER_CLASS_SEPARATOR_CHAR = '$'; + + /** + *

The inner class separator String: {@code "$"}.

+ */ + public static final String INNER_CLASS_SEPARATOR = String.valueOf(INNER_CLASS_SEPARATOR_CHAR); + + /** + * Maps primitive {@code Class}es to their corresponding wrapper {@code Class}. + */ + private static final Map, Class> primitiveWrapperMap = new HashMap, Class>(); + static { + primitiveWrapperMap.put(Boolean.TYPE, Boolean.class); + primitiveWrapperMap.put(Byte.TYPE, Byte.class); + primitiveWrapperMap.put(Character.TYPE, Character.class); + primitiveWrapperMap.put(Short.TYPE, Short.class); + primitiveWrapperMap.put(Integer.TYPE, Integer.class); + primitiveWrapperMap.put(Long.TYPE, Long.class); + primitiveWrapperMap.put(Double.TYPE, Double.class); + primitiveWrapperMap.put(Float.TYPE, Float.class); + primitiveWrapperMap.put(Void.TYPE, Void.TYPE); + } + + /** + * Maps wrapper {@code Class}es to their corresponding primitive types. + */ + private static final Map, Class> wrapperPrimitiveMap = new HashMap, Class>(); + static { + for (Class primitiveClass : primitiveWrapperMap.keySet()) { + Class wrapperClass = primitiveWrapperMap.get(primitiveClass); + if (!primitiveClass.equals(wrapperClass)) { + wrapperPrimitiveMap.put(wrapperClass, primitiveClass); + } + } + } + + /** + * Maps a primitive class name to its corresponding abbreviation used in array class names. + */ + private static final Map abbreviationMap = new HashMap(); + + /** + * Maps an abbreviation used in array class names to corresponding primitive class name. + */ + private static final Map reverseAbbreviationMap = new HashMap(); + + /** + * Add primitive type abbreviation to maps of abbreviations. + * + * @param primitive Canonical name of primitive type + * @param abbreviation Corresponding abbreviation of primitive type + */ + private static void addAbbreviation(String primitive, String abbreviation) { + abbreviationMap.put(primitive, abbreviation); + reverseAbbreviationMap.put(abbreviation, primitive); + } + + /** + * Feed abbreviation maps + */ + static { + addAbbreviation("int", "I"); + addAbbreviation("boolean", "Z"); + addAbbreviation("float", "F"); + addAbbreviation("long", "J"); + addAbbreviation("short", "S"); + addAbbreviation("byte", "B"); + addAbbreviation("double", "D"); + addAbbreviation("char", "C"); + } + + /** + *

ClassUtils instances should NOT be constructed in standard programming. + * Instead, the class should be used as + * {@code ClassUtils.getShortClassName(cls)}.

+ * + *

This constructor is public to permit tools that require a JavaBean + * instance to operate.

+ */ + public ClassUtils() { + super(); + } + + // Short class name + // ---------------------------------------------------------------------- + /** + *

Gets the class name minus the package name for an {@code Object}.

+ * + * @param object the class to get the short name for, may be null + * @param valueIfNull the value to return if null + * @return the class name of the object without the package name, or the null value + */ + public static String getShortClassName(Object object, String valueIfNull) { + if (object == null) { + return valueIfNull; + } + return getShortClassName(object.getClass()); + } + + /** + *

Gets the class name minus the package name from a {@code Class}.

+ * + *

Consider using the Java 5 API {@link Class#getSimpleName()} instead. + * The one known difference is that this code will return {@code "Map.Entry"} while + * the {@code java.lang.Class} variant will simply return {@code "Entry"}.

+ * + * @param cls the class to get the short name for. + * @return the class name without the package name or an empty string + */ + public static String getShortClassName(Class cls) { + if (cls == null) { + return StringUtils.EMPTY; + } + return getShortClassName(cls.getName()); + } + + /** + *

Gets the class name minus the package name from a String.

+ * + *

The string passed in is assumed to be a class name - it is not checked.

+ + *

Note that this method differs from Class.getSimpleName() in that this will + * return {@code "Map.Entry"} whilst the {@code java.lang.Class} variant will simply + * return {@code "Entry"}.

+ * + * @param className the className to get the short name for + * @return the class name of the class without the package name or an empty string + */ + public static String getShortClassName(String className) { + if (StringUtils.isEmpty(className)) { + return StringUtils.EMPTY; + } + + StringBuilder arrayPrefix = new StringBuilder(); + + // Handle array encoding + if (className.startsWith("[")) { + while (className.charAt(0) == '[') { + className = className.substring(1); + arrayPrefix.append("[]"); + } + // Strip Object type encoding + if (className.charAt(0) == 'L' && className.charAt(className.length() - 1) == ';') { + className = className.substring(1, className.length() - 1); + } + } + + if (reverseAbbreviationMap.containsKey(className)) { + className = reverseAbbreviationMap.get(className); + } + + int lastDotIdx = className.lastIndexOf(PACKAGE_SEPARATOR_CHAR); + int innerIdx = className.indexOf( + INNER_CLASS_SEPARATOR_CHAR, lastDotIdx == -1 ? 0 : lastDotIdx + 1); + String out = className.substring(lastDotIdx + 1); + if (innerIdx != -1) { + out = out.replace(INNER_CLASS_SEPARATOR_CHAR, PACKAGE_SEPARATOR_CHAR); + } + return out + arrayPrefix; + } + + /** + *

Null-safe version of aClass.getSimpleName()

+ * + * @param cls the class for which to get the simple name. + * @return the simple class name. + * @since 3.0 + * @see Class#getSimpleName() + */ + public static String getSimpleName(Class cls) { + if (cls == null) { + return StringUtils.EMPTY; + } + return cls.getSimpleName(); + } + + /** + *

Null-safe version of aClass.getSimpleName()

+ * + * @param object the object for which to get the simple class name. + * @param valueIfNull the value to return if object is null + * @return the simple class name. + * @since 3.0 + * @see Class#getSimpleName() + */ + public static String getSimpleName(Object object, String valueIfNull) { + if (object == null) { + return valueIfNull; + } + return getSimpleName(object.getClass()); + } + + // Package name + // ---------------------------------------------------------------------- + /** + *

Gets the package name of an {@code Object}.

+ * + * @param object the class to get the package name for, may be null + * @param valueIfNull the value to return if null + * @return the package name of the object, or the null value + */ + public static String getPackageName(Object object, String valueIfNull) { + if (object == null) { + return valueIfNull; + } + return getPackageName(object.getClass()); + } + + /** + *

Gets the package name of a {@code Class}.

+ * + * @param cls the class to get the package name for, may be {@code null}. + * @return the package name or an empty string + */ + public static String getPackageName(Class cls) { + if (cls == null) { + return StringUtils.EMPTY; + } + return getPackageName(cls.getName()); + } + + /** + *

Gets the package name from a {@code String}.

+ * + *

The string passed in is assumed to be a class name - it is not checked.

+ *

If the class is unpackaged, return an empty string.

+ * + * @param className the className to get the package name for, may be {@code null} + * @return the package name or an empty string + */ + public static String getPackageName(String className) { + if (StringUtils.isEmpty(className)) { + return StringUtils.EMPTY; + } + + // Strip array encoding + while (className.charAt(0) == '[') { + className = className.substring(1); + } + // Strip Object type encoding + if (className.charAt(0) == 'L' && className.charAt(className.length() - 1) == ';') { + className = className.substring(1); + } + + int i = className.lastIndexOf(PACKAGE_SEPARATOR_CHAR); + if (i == -1) { + return StringUtils.EMPTY; + } + return className.substring(0, i); + } + + // Superclasses/Superinterfaces + // ---------------------------------------------------------------------- + /** + *

Gets a {@code List} of superclasses for the given class.

+ * + * @param cls the class to look up, may be {@code null} + * @return the {@code List} of superclasses in order going up from this one + * {@code null} if null input + */ + public static List> getAllSuperclasses(Class cls) { + if (cls == null) { + return null; + } + List> classes = new ArrayList>(); + Class superclass = cls.getSuperclass(); + while (superclass != null) { + classes.add(superclass); + superclass = superclass.getSuperclass(); + } + return classes; + } + + /** + *

Gets a {@code List} of all interfaces implemented by the given + * class and its superclasses.

+ * + *

The order is determined by looking through each interface in turn as + * declared in the source file and following its hierarchy up. Then each + * superclass is considered in the same way. Later duplicates are ignored, + * so the order is maintained.

+ * + * @param cls the class to look up, may be {@code null} + * @return the {@code List} of interfaces in order, + * {@code null} if null input + */ + public static List> getAllInterfaces(Class cls) { + if (cls == null) { + return null; + } + + LinkedHashSet> interfacesFound = new LinkedHashSet>(); + getAllInterfaces(cls, interfacesFound); + + return new ArrayList>(interfacesFound); + } + + /** + * Get the interfaces for the specified class. + * + * @param cls the class to look up, may be {@code null} + * @param interfacesFound the {@code Set} of interfaces for the class + */ + private static void getAllInterfaces(Class cls, HashSet> interfacesFound) { + while (cls != null) { + Class[] interfaces = cls.getInterfaces(); + + for (Class i : interfaces) { + if (interfacesFound.add(i)) { + getAllInterfaces(i, interfacesFound); + } + } + + cls = cls.getSuperclass(); + } + } + + // Convert list + // ---------------------------------------------------------------------- + /** + *

Given a {@code List} of class names, this method converts them into classes.

+ * + *

A new {@code List} is returned. If the class name cannot be found, {@code null} + * is stored in the {@code List}. If the class name in the {@code List} is + * {@code null}, {@code null} is stored in the output {@code List}.

+ * + * @param classNames the classNames to change + * @return a {@code List} of Class objects corresponding to the class names, + * {@code null} if null input + * @throws ClassCastException if classNames contains a non String entry + */ + public static List> convertClassNamesToClasses(List classNames) { + if (classNames == null) { + return null; + } + List> classes = new ArrayList>(classNames.size()); + for (String className : classNames) { + try { + classes.add(Class.forName(className)); + } catch (Exception ex) { + classes.add(null); + } + } + return classes; + } + + /** + *

Given a {@code List} of {@code Class} objects, this method converts + * them into class names.

+ * + *

A new {@code List} is returned. {@code null} objects will be copied into + * the returned list as {@code null}.

+ * + * @param classes the classes to change + * @return a {@code List} of class names corresponding to the Class objects, + * {@code null} if null input + * @throws ClassCastException if {@code classes} contains a non-{@code Class} entry + */ + public static List convertClassesToClassNames(List> classes) { + if (classes == null) { + return null; + } + List classNames = new ArrayList(classes.size()); + for (Class cls : classes) { + if (cls == null) { + classNames.add(null); + } else { + classNames.add(cls.getName()); + } + } + return classNames; + } + + // Is assignable + // ---------------------------------------------------------------------- + /** + *

Checks if an array of Classes can be assigned to another array of Classes.

+ * + *

This method calls {@link #isAssignable(Class, Class) isAssignable} for each + * Class pair in the input arrays. It can be used to check if a set of arguments + * (the first parameter) are suitably compatible with a set of method parameter types + * (the second parameter).

+ * + *

Unlike the {@link Class#isAssignableFrom(java.lang.Class)} method, this + * method takes into account widenings of primitive classes and + * {@code null}s.

+ * + *

Primitive widenings allow an int to be assigned to a {@code long}, + * {@code float} or {@code double}. This method returns the correct + * result for these cases.

+ * + *

{@code Null} may be assigned to any reference type. This method will + * return {@code true} if {@code null} is passed in and the toClass is + * non-primitive.

+ * + *

Specifically, this method tests whether the type represented by the + * specified {@code Class} parameter can be converted to the type + * represented by this {@code Class} object via an identity conversion + * widening primitive or widening reference conversion. See + * The Java Language Specification, + * sections 5.1.1, 5.1.2 and 5.1.4 for details.

+ * + *

Since Lang 3.0, this method will default behavior for + * calculating assignability between primitive and wrapper types corresponding + * to the running Java version; i.e. autoboxing will be the default + * behavior in VMs running Java versions >= 1.5.

+ * + * @param classArray the array of Classes to check, may be {@code null} + * @param toClassArray the array of Classes to try to assign into, may be {@code null} + * @return {@code true} if assignment possible + */ + public static boolean isAssignable(Class[] classArray, Class... toClassArray) { + return isAssignable(classArray, toClassArray, SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_1_5)); + } + + /** + *

Checks if an array of Classes can be assigned to another array of Classes.

+ * + *

This method calls {@link #isAssignable(Class, Class) isAssignable} for each + * Class pair in the input arrays. It can be used to check if a set of arguments + * (the first parameter) are suitably compatible with a set of method parameter types + * (the second parameter).

+ * + *

Unlike the {@link Class#isAssignableFrom(java.lang.Class)} method, this + * method takes into account widenings of primitive classes and + * {@code null}s.

+ * + *

Primitive widenings allow an int to be assigned to a {@code long}, + * {@code float} or {@code double}. This method returns the correct + * result for these cases.

+ * + *

{@code Null} may be assigned to any reference type. This method will + * return {@code true} if {@code null} is passed in and the toClass is + * non-primitive.

+ * + *

Specifically, this method tests whether the type represented by the + * specified {@code Class} parameter can be converted to the type + * represented by this {@code Class} object via an identity conversion + * widening primitive or widening reference conversion. See + * The Java Language Specification, + * sections 5.1.1, 5.1.2 and 5.1.4 for details.

+ * + * @param classArray the array of Classes to check, may be {@code null} + * @param toClassArray the array of Classes to try to assign into, may be {@code null} + * @param autoboxing whether to use implicit autoboxing/unboxing between primitives and wrappers + * @return {@code true} if assignment possible + */ + public static boolean isAssignable(Class[] classArray, Class[] toClassArray, boolean autoboxing) { + if (ArrayUtils.isSameLength(classArray, toClassArray) == false) { + return false; + } + if (classArray == null) { + classArray = ArrayUtils.EMPTY_CLASS_ARRAY; + } + if (toClassArray == null) { + toClassArray = ArrayUtils.EMPTY_CLASS_ARRAY; + } + for (int i = 0; i < classArray.length; i++) { + if (isAssignable(classArray[i], toClassArray[i], autoboxing) == false) { + return false; + } + } + return true; + } + + /** + * Returns whether the given {@code type} is a primitive or primitive wrapper ({@link Boolean}, {@link Byte}, {@link Character}, + * {@link Short}, {@link Integer}, {@link Long}, {@link Double}, {@link Float}). + * + * @param type + * The class to query or null. + * @return true if the given {@code type} is a primitive or primitive wrapper ({@link Boolean}, {@link Byte}, {@link Character}, + * {@link Short}, {@link Integer}, {@link Long}, {@link Double}, {@link Float}). + * @since 3.1 + */ + public static boolean isPrimitiveOrWrapper(Class type) { + if (type == null) { + return false; + } + return type.isPrimitive() || isPrimitiveWrapper(type); + } + + /** + * Returns whether the given {@code type} is a primitive wrapper ({@link Boolean}, {@link Byte}, {@link Character}, {@link Short}, + * {@link Integer}, {@link Long}, {@link Double}, {@link Float}). + * + * @param type + * The class to query or null. + * @return true if the given {@code type} is a primitive wrapper ({@link Boolean}, {@link Byte}, {@link Character}, {@link Short}, + * {@link Integer}, {@link Long}, {@link Double}, {@link Float}). + * @since 3.1 + */ + public static boolean isPrimitiveWrapper(Class type) { + return wrapperPrimitiveMap.containsKey(type); + } + + /** + *

Checks if one {@code Class} can be assigned to a variable of + * another {@code Class}.

+ * + *

Unlike the {@link Class#isAssignableFrom(java.lang.Class)} method, + * this method takes into account widenings of primitive classes and + * {@code null}s.

+ * + *

Primitive widenings allow an int to be assigned to a long, float or + * double. This method returns the correct result for these cases.

+ * + *

{@code Null} may be assigned to any reference type. This method + * will return {@code true} if {@code null} is passed in and the + * toClass is non-primitive.

+ * + *

Specifically, this method tests whether the type represented by the + * specified {@code Class} parameter can be converted to the type + * represented by this {@code Class} object via an identity conversion + * widening primitive or widening reference conversion. See + * The Java Language Specification, + * sections 5.1.1, 5.1.2 and 5.1.4 for details.

+ * + *

Since Lang 3.0, this method will default behavior for + * calculating assignability between primitive and wrapper types corresponding + * to the running Java version; i.e. autoboxing will be the default + * behavior in VMs running Java versions >= 1.5.

+ * + * @param cls the Class to check, may be null + * @param toClass the Class to try to assign into, returns false if null + * @return {@code true} if assignment possible + */ + public static boolean isAssignable(Class cls, Class toClass) { + return isAssignable(cls, toClass, SystemUtils.isJavaVersionAtLeast(JavaVersion.JAVA_1_5)); + } + + /** + *

Checks if one {@code Class} can be assigned to a variable of + * another {@code Class}.

+ * + *

Unlike the {@link Class#isAssignableFrom(java.lang.Class)} method, + * this method takes into account widenings of primitive classes and + * {@code null}s.

+ * + *

Primitive widenings allow an int to be assigned to a long, float or + * double. This method returns the correct result for these cases.

+ * + *

{@code Null} may be assigned to any reference type. This method + * will return {@code true} if {@code null} is passed in and the + * toClass is non-primitive.

+ * + *

Specifically, this method tests whether the type represented by the + * specified {@code Class} parameter can be converted to the type + * represented by this {@code Class} object via an identity conversion + * widening primitive or widening reference conversion. See + * The Java Language Specification, + * sections 5.1.1, 5.1.2 and 5.1.4 for details.

+ * + * @param cls the Class to check, may be null + * @param toClass the Class to try to assign into, returns false if null + * @param autoboxing whether to use implicit autoboxing/unboxing between primitives and wrappers + * @return {@code true} if assignment possible + */ + public static boolean isAssignable(Class cls, Class toClass, boolean autoboxing) { + if (toClass == null) { + return false; + } + // have to check for null, as isAssignableFrom doesn't + if (cls == null) { + return !toClass.isPrimitive(); + } + //autoboxing: + if (autoboxing) { + if (cls.isPrimitive() && !toClass.isPrimitive()) { + cls = primitiveToWrapper(cls); + if (cls == null) { + return false; + } + } + if (toClass.isPrimitive() && !cls.isPrimitive()) { + cls = wrapperToPrimitive(cls); + if (cls == null) { + return false; + } + } + } + if (cls.equals(toClass)) { + return true; + } + if (cls.isPrimitive()) { + if (toClass.isPrimitive() == false) { + return false; + } + if (Integer.TYPE.equals(cls)) { + return Long.TYPE.equals(toClass) + || Float.TYPE.equals(toClass) + || Double.TYPE.equals(toClass); + } + if (Long.TYPE.equals(cls)) { + return Float.TYPE.equals(toClass) + || Double.TYPE.equals(toClass); + } + if (Boolean.TYPE.equals(cls)) { + return false; + } + if (Double.TYPE.equals(cls)) { + return false; + } + if (Float.TYPE.equals(cls)) { + return Double.TYPE.equals(toClass); + } + if (Character.TYPE.equals(cls)) { + return Integer.TYPE.equals(toClass) + || Long.TYPE.equals(toClass) + || Float.TYPE.equals(toClass) + || Double.TYPE.equals(toClass); + } + if (Short.TYPE.equals(cls)) { + return Integer.TYPE.equals(toClass) + || Long.TYPE.equals(toClass) + || Float.TYPE.equals(toClass) + || Double.TYPE.equals(toClass); + } + if (Byte.TYPE.equals(cls)) { + return Short.TYPE.equals(toClass) + || Integer.TYPE.equals(toClass) + || Long.TYPE.equals(toClass) + || Float.TYPE.equals(toClass) + || Double.TYPE.equals(toClass); + } + // should never get here + return false; + } + return toClass.isAssignableFrom(cls); + } + + /** + *

Converts the specified primitive Class object to its corresponding + * wrapper Class object.

+ * + *

NOTE: From v2.2, this method handles {@code Void.TYPE}, + * returning {@code Void.TYPE}.

+ * + * @param cls the class to convert, may be null + * @return the wrapper class for {@code cls} or {@code cls} if + * {@code cls} is not a primitive. {@code null} if null input. + * @since 2.1 + */ + public static Class primitiveToWrapper(Class cls) { + Class convertedClass = cls; + if (cls != null && cls.isPrimitive()) { + convertedClass = primitiveWrapperMap.get(cls); + } + return convertedClass; + } + + /** + *

Converts the specified array of primitive Class objects to an array of + * its corresponding wrapper Class objects.

+ * + * @param classes the class array to convert, may be null or empty + * @return an array which contains for each given class, the wrapper class or + * the original class if class is not a primitive. {@code null} if null input. + * Empty array if an empty array passed in. + * @since 2.1 + */ + public static Class[] primitivesToWrappers(Class... classes) { + if (classes == null) { + return null; + } + + if (classes.length == 0) { + return classes; + } + + Class[] convertedClasses = new Class[classes.length]; + for (int i = 0; i < classes.length; i++) { + convertedClasses[i] = primitiveToWrapper(classes[i]); + } + return convertedClasses; + } + + /** + *

Converts the specified wrapper class to its corresponding primitive + * class.

+ * + *

This method is the counter part of {@code primitiveToWrapper()}. + * If the passed in class is a wrapper class for a primitive type, this + * primitive type will be returned (e.g. {@code Integer.TYPE} for + * {@code Integer.class}). For other classes, or if the parameter is + * null, the return value is null.

+ * + * @param cls the class to convert, may be null + * @return the corresponding primitive type if {@code cls} is a + * wrapper class, null otherwise + * @see #primitiveToWrapper(Class) + * @since 2.4 + */ + public static Class wrapperToPrimitive(Class cls) { + return wrapperPrimitiveMap.get(cls); + } + + /** + *

Converts the specified array of wrapper Class objects to an array of + * its corresponding primitive Class objects.

+ * + *

This method invokes {@code wrapperToPrimitive()} for each element + * of the passed in array.

+ * + * @param classes the class array to convert, may be null or empty + * @return an array which contains for each given class, the primitive class or + * null if the original class is not a wrapper class. {@code null} if null input. + * Empty array if an empty array passed in. + * @see #wrapperToPrimitive(Class) + * @since 2.4 + */ + public static Class[] wrappersToPrimitives(Class... classes) { + if (classes == null) { + return null; + } + + if (classes.length == 0) { + return classes; + } + + Class[] convertedClasses = new Class[classes.length]; + for (int i = 0; i < classes.length; i++) { + convertedClasses[i] = wrapperToPrimitive(classes[i]); + } + return convertedClasses; + } + + // Inner class + // ---------------------------------------------------------------------- + /** + *

Is the specified class an inner class or static nested class.

+ * + * @param cls the class to check, may be null + * @return {@code true} if the class is an inner or static nested class, + * false if not or {@code null} + */ + public static boolean isInnerClass(Class cls) { + return cls != null && cls.getEnclosingClass() != null; + } + + // Class loading + // ---------------------------------------------------------------------- + /** + * Returns the class represented by {@code className} using the + * {@code classLoader}. This implementation supports the syntaxes + * "{@code java.util.Map.Entry[]}", "{@code java.util.Map$Entry[]}", + * "{@code [Ljava.util.Map.Entry;}", and "{@code [Ljava.util.Map$Entry;}". + * + * @param classLoader the class loader to use to load the class + * @param className the class name + * @param initialize whether the class must be initialized + * @return the class represented by {@code className} using the {@code classLoader} + * @throws ClassNotFoundException if the class is not found + */ + public static Class getClass( + ClassLoader classLoader, String className, boolean initialize) throws ClassNotFoundException { + try { + Class clazz; + if (abbreviationMap.containsKey(className)) { + String clsName = "[" + abbreviationMap.get(className); + clazz = Class.forName(clsName, initialize, classLoader).getComponentType(); + } else { + clazz = Class.forName(toCanonicalName(className), initialize, classLoader); + } + return clazz; + } catch (ClassNotFoundException ex) { + // allow path separators (.) as inner class name separators + int lastDotIndex = className.lastIndexOf(PACKAGE_SEPARATOR_CHAR); + + if (lastDotIndex != -1) { + try { + return getClass(classLoader, className.substring(0, lastDotIndex) + + INNER_CLASS_SEPARATOR_CHAR + className.substring(lastDotIndex + 1), + initialize); + } catch (ClassNotFoundException ex2) { // NOPMD + // ignore exception + } + } + + throw ex; + } + } + + /** + * Returns the (initialized) class represented by {@code className} + * using the {@code classLoader}. This implementation supports + * the syntaxes "{@code java.util.Map.Entry[]}", + * "{@code java.util.Map$Entry[]}", "{@code [Ljava.util.Map.Entry;}", + * and "{@code [Ljava.util.Map$Entry;}". + * + * @param classLoader the class loader to use to load the class + * @param className the class name + * @return the class represented by {@code className} using the {@code classLoader} + * @throws ClassNotFoundException if the class is not found + */ + public static Class getClass(ClassLoader classLoader, String className) throws ClassNotFoundException { + return getClass(classLoader, className, true); + } + + /** + * Returns the (initialized) class represented by {@code className} + * using the current thread's context class loader. This implementation + * supports the syntaxes "{@code java.util.Map.Entry[]}", + * "{@code java.util.Map$Entry[]}", "{@code [Ljava.util.Map.Entry;}", + * and "{@code [Ljava.util.Map$Entry;}". + * + * @param className the class name + * @return the class represented by {@code className} using the current thread's context class loader + * @throws ClassNotFoundException if the class is not found + */ + public static Class getClass(String className) throws ClassNotFoundException { + return getClass(className, true); + } + + /** + * Returns the class represented by {@code className} using the + * current thread's context class loader. This implementation supports the + * syntaxes "{@code java.util.Map.Entry[]}", "{@code java.util.Map$Entry[]}", + * "{@code [Ljava.util.Map.Entry;}", and "{@code [Ljava.util.Map$Entry;}". + * + * @param className the class name + * @param initialize whether the class must be initialized + * @return the class represented by {@code className} using the current thread's context class loader + * @throws ClassNotFoundException if the class is not found + */ + public static Class getClass(String className, boolean initialize) throws ClassNotFoundException { + ClassLoader contextCL = Thread.currentThread().getContextClassLoader(); + ClassLoader loader = contextCL == null ? ClassUtils.class.getClassLoader() : contextCL; + return getClass(loader, className, initialize); + } + + // Public method + // ---------------------------------------------------------------------- + /** + *

Returns the desired Method much like {@code Class.getMethod}, however + * it ensures that the returned Method is from a public class or interface and not + * from an anonymous inner class. This means that the Method is invokable and + * doesn't fall foul of Java bug + * 4071957). + * + *

Set set = Collections.unmodifiableSet(...);
+     *  Method method = ClassUtils.getPublicMethod(set.getClass(), "isEmpty",  new Class[0]);
+     *  Object result = method.invoke(set, new Object[]);
+ *

+ * + * @param cls the class to check, not null + * @param methodName the name of the method + * @param parameterTypes the list of parameters + * @return the method + * @throws NullPointerException if the class is null + * @throws SecurityException if a a security violation occured + * @throws NoSuchMethodException if the method is not found in the given class + * or if the metothod doen't conform with the requirements + */ + public static Method getPublicMethod(Class cls, String methodName, Class... parameterTypes) + throws SecurityException, NoSuchMethodException { + + Method declaredMethod = cls.getMethod(methodName, parameterTypes); + if (Modifier.isPublic(declaredMethod.getDeclaringClass().getModifiers())) { + return declaredMethod; + } + + List> candidateClasses = new ArrayList>(); + candidateClasses.addAll(getAllInterfaces(cls)); + candidateClasses.addAll(getAllSuperclasses(cls)); + + for (Class candidateClass : candidateClasses) { + if (!Modifier.isPublic(candidateClass.getModifiers())) { + continue; + } + Method candidateMethod; + try { + candidateMethod = candidateClass.getMethod(methodName, parameterTypes); + } catch (NoSuchMethodException ex) { + continue; + } + if (Modifier.isPublic(candidateMethod.getDeclaringClass().getModifiers())) { + return candidateMethod; + } + } + + throw new NoSuchMethodException("Can't find a public method for " + + methodName + " " + ArrayUtils.toString(parameterTypes)); + } + + // ---------------------------------------------------------------------- + /** + * Converts a class name to a JLS style class name. + * + * @param className the class name + * @return the converted name + */ + private static String toCanonicalName(String className) { + className = StringUtils.deleteWhitespace(className); + if (className == null) { + throw new NullPointerException("className must not be null."); + } else if (className.endsWith("[]")) { + StringBuilder classNameBuffer = new StringBuilder(); + while (className.endsWith("[]")) { + className = className.substring(0, className.length() - 2); + classNameBuffer.append("["); + } + String abbreviation = abbreviationMap.get(className); + if (abbreviation != null) { + classNameBuffer.append(abbreviation); + } else { + classNameBuffer.append("L").append(className).append(";"); + } + className = classNameBuffer.toString(); + } + return className; + } + + /** + *

Converts an array of {@code Object} in to an array of {@code Class} objects. + * If any of these objects is null, a null element will be inserted into the array.

+ * + *

This method returns {@code null} for a {@code null} input array.

+ * + * @param array an {@code Object} array + * @return a {@code Class} array, {@code null} if null array input + * @since 2.4 + */ + public static Class[] toClass(Object... array) { + if (array == null) { + return null; + } else if (array.length == 0) { + return ArrayUtils.EMPTY_CLASS_ARRAY; + } + Class[] classes = new Class[array.length]; + for (int i = 0; i < array.length; i++) { + classes[i] = array[i] == null ? null : array[i].getClass(); + } + return classes; + } + + // Short canonical name + // ---------------------------------------------------------------------- + /** + *

Gets the canonical name minus the package name for an {@code Object}.

+ * + * @param object the class to get the short name for, may be null + * @param valueIfNull the value to return if null + * @return the canonical name of the object without the package name, or the null value + * @since 2.4 + */ + public static String getShortCanonicalName(Object object, String valueIfNull) { + if (object == null) { + return valueIfNull; + } + return getShortCanonicalName(object.getClass().getName()); + } + + /** + *

Gets the canonical name minus the package name from a {@code Class}.

+ * + * @param cls the class to get the short name for. + * @return the canonical name without the package name or an empty string + * @since 2.4 + */ + public static String getShortCanonicalName(Class cls) { + if (cls == null) { + return StringUtils.EMPTY; + } + return getShortCanonicalName(cls.getName()); + } + + /** + *

Gets the canonical name minus the package name from a String.

+ * + *

The string passed in is assumed to be a canonical name - it is not checked.

+ * + * @param canonicalName the class name to get the short name for + * @return the canonical name of the class without the package name or an empty string + * @since 2.4 + */ + public static String getShortCanonicalName(String canonicalName) { + return ClassUtils.getShortClassName(getCanonicalName(canonicalName)); + } + + // Package name + // ---------------------------------------------------------------------- + /** + *

Gets the package name from the canonical name of an {@code Object}.

+ * + * @param object the class to get the package name for, may be null + * @param valueIfNull the value to return if null + * @return the package name of the object, or the null value + * @since 2.4 + */ + public static String getPackageCanonicalName(Object object, String valueIfNull) { + if (object == null) { + return valueIfNull; + } + return getPackageCanonicalName(object.getClass().getName()); + } + + /** + *

Gets the package name from the canonical name of a {@code Class}.

+ * + * @param cls the class to get the package name for, may be {@code null}. + * @return the package name or an empty string + * @since 2.4 + */ + public static String getPackageCanonicalName(Class cls) { + if (cls == null) { + return StringUtils.EMPTY; + } + return getPackageCanonicalName(cls.getName()); + } + + /** + *

Gets the package name from the canonical name.

+ * + *

The string passed in is assumed to be a canonical name - it is not checked.

+ *

If the class is unpackaged, return an empty string.

+ * + * @param canonicalName the canonical name to get the package name for, may be {@code null} + * @return the package name or an empty string + * @since 2.4 + */ + public static String getPackageCanonicalName(String canonicalName) { + return ClassUtils.getPackageName(getCanonicalName(canonicalName)); + } + + /** + *

Converts a given name of class into canonical format. + * If name of class is not a name of array class it returns + * unchanged name.

+ *

Example: + *

    + *
  • {@code getCanonicalName("[I") = "int[]"}
  • + *
  • {@code getCanonicalName("[Ljava.lang.String;") = "java.lang.String[]"}
  • + *
  • {@code getCanonicalName("java.lang.String") = "java.lang.String"}
  • + *
+ *

+ * + * @param className the name of class + * @return canonical form of class name + * @since 2.4 + */ + private static String getCanonicalName(String className) { + className = StringUtils.deleteWhitespace(className); + if (className == null) { + return null; + } else { + int dim = 0; + while (className.startsWith("[")) { + dim++; + className = className.substring(1); + } + if (dim < 1) { + return className; + } else { + if (className.startsWith("L")) { + className = className.substring( + 1, + className.endsWith(";") + ? className.length() - 1 + : className.length()); + } else { + if (className.length() > 0) { + className = reverseAbbreviationMap.get(className.substring(0, 1)); + } + } + StringBuilder canonicalClassNameBuffer = new StringBuilder(className); + for (int i = 0; i < dim; i++) { + canonicalClassNameBuffer.append("[]"); + } + return canonicalClassNameBuffer.toString(); + } + } + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/ClassUtilsTest.java b/ApacheCommonsLang/org/apache/commons/lang3/ClassUtilsTest.java new file mode 100644 index 0000000..b7716ea --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/ClassUtilsTest.java @@ -0,0 +1,1165 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3; + +import static org.apache.commons.lang3.JavaVersion.JAVA_1_5; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import junit.framework.TestCase; + +/** + * Unit tests {@link org.apache.commons.lang3.ClassUtils}. + * + * @version $Id: ClassUtilsTest.java 1199730 2011-11-09 13:00:47Z sebb $ + */ +public class ClassUtilsTest extends TestCase { + + public ClassUtilsTest(String name) { + super(name); + } + + private static class Inner { + private class DeeplyNested{} + } + + //----------------------------------------------------------------------- + public void testConstructor() { + assertNotNull(new ClassUtils()); + Constructor[] cons = ClassUtils.class.getDeclaredConstructors(); + assertEquals(1, cons.length); + assertEquals(true, Modifier.isPublic(cons[0].getModifiers())); + assertEquals(true, Modifier.isPublic(ClassUtils.class.getModifiers())); + assertEquals(false, Modifier.isFinal(ClassUtils.class.getModifiers())); + } + + // ------------------------------------------------------------------------- + public void test_getShortClassName_Object() { + assertEquals("ClassUtils", ClassUtils.getShortClassName(new ClassUtils(), "")); + assertEquals("ClassUtilsTest.Inner", ClassUtils.getShortClassName(new Inner(), "")); + assertEquals("String", ClassUtils.getShortClassName("hello", "")); + assertEquals("", ClassUtils.getShortClassName(null, "")); + + // Inner types + class Named extends Object {} + assertEquals("ClassUtilsTest.1", ClassUtils.getShortClassName(new Object(){}, "")); + assertEquals("ClassUtilsTest.1Named", ClassUtils.getShortClassName(new Named(), "")); + assertEquals("ClassUtilsTest.Inner", ClassUtils.getShortClassName(new Inner(), "")); + } + + public void test_getShortClassName_Class() { + assertEquals("ClassUtils", ClassUtils.getShortClassName(ClassUtils.class)); + assertEquals("Map.Entry", ClassUtils.getShortClassName(Map.Entry.class)); + assertEquals("", ClassUtils.getShortClassName((Class) null)); + + // LANG-535 + assertEquals("String[]", ClassUtils.getShortClassName(String[].class)); + assertEquals("Map.Entry[]", ClassUtils.getShortClassName(Map.Entry[].class)); + + // Primitives + assertEquals("boolean", ClassUtils.getShortClassName(boolean.class)); + assertEquals("byte", ClassUtils.getShortClassName(byte.class)); + assertEquals("char", ClassUtils.getShortClassName(char.class)); + assertEquals("short", ClassUtils.getShortClassName(short.class)); + assertEquals("int", ClassUtils.getShortClassName(int.class)); + assertEquals("long", ClassUtils.getShortClassName(long.class)); + assertEquals("float", ClassUtils.getShortClassName(float.class)); + assertEquals("double", ClassUtils.getShortClassName(double.class)); + + // Primitive Arrays + assertEquals("boolean[]", ClassUtils.getShortClassName(boolean[].class)); + assertEquals("byte[]", ClassUtils.getShortClassName(byte[].class)); + assertEquals("char[]", ClassUtils.getShortClassName(char[].class)); + assertEquals("short[]", ClassUtils.getShortClassName(short[].class)); + assertEquals("int[]", ClassUtils.getShortClassName(int[].class)); + assertEquals("long[]", ClassUtils.getShortClassName(long[].class)); + assertEquals("float[]", ClassUtils.getShortClassName(float[].class)); + assertEquals("double[]", ClassUtils.getShortClassName(double[].class)); + + // Arrays of arrays of ... + assertEquals("String[][]", ClassUtils.getShortClassName(String[][].class)); + assertEquals("String[][][]", ClassUtils.getShortClassName(String[][][].class)); + assertEquals("String[][][][]", ClassUtils.getShortClassName(String[][][][].class)); + + // Inner types + class Named extends Object {} + assertEquals("ClassUtilsTest.2", ClassUtils.getShortClassName(new Object(){}.getClass())); + assertEquals("ClassUtilsTest.2Named", ClassUtils.getShortClassName(Named.class)); + assertEquals("ClassUtilsTest.Inner", ClassUtils.getShortClassName(Inner.class)); + } + + + + public void test_getShortClassName_String() { + assertEquals("ClassUtils", ClassUtils.getShortClassName(ClassUtils.class.getName())); + assertEquals("Map.Entry", ClassUtils.getShortClassName(Map.Entry.class.getName())); + assertEquals("", ClassUtils.getShortClassName((String) null)); + assertEquals("", ClassUtils.getShortClassName("")); + } + + public void test_getSimpleName_Class() { + assertEquals("ClassUtils", ClassUtils.getSimpleName(ClassUtils.class)); + assertEquals("Entry", ClassUtils.getSimpleName(Map.Entry.class)); + assertEquals("", ClassUtils.getSimpleName((Class) null)); + + // LANG-535 + assertEquals("String[]", ClassUtils.getSimpleName(String[].class)); + assertEquals("Entry[]", ClassUtils.getSimpleName(Map.Entry[].class)); + + // Primitives + assertEquals("boolean", ClassUtils.getSimpleName(boolean.class)); + assertEquals("byte", ClassUtils.getSimpleName(byte.class)); + assertEquals("char", ClassUtils.getSimpleName(char.class)); + assertEquals("short", ClassUtils.getSimpleName(short.class)); + assertEquals("int", ClassUtils.getSimpleName(int.class)); + assertEquals("long", ClassUtils.getSimpleName(long.class)); + assertEquals("float", ClassUtils.getSimpleName(float.class)); + assertEquals("double", ClassUtils.getSimpleName(double.class)); + + // Primitive Arrays + assertEquals("boolean[]", ClassUtils.getSimpleName(boolean[].class)); + assertEquals("byte[]", ClassUtils.getSimpleName(byte[].class)); + assertEquals("char[]", ClassUtils.getSimpleName(char[].class)); + assertEquals("short[]", ClassUtils.getSimpleName(short[].class)); + assertEquals("int[]", ClassUtils.getSimpleName(int[].class)); + assertEquals("long[]", ClassUtils.getSimpleName(long[].class)); + assertEquals("float[]", ClassUtils.getSimpleName(float[].class)); + assertEquals("double[]", ClassUtils.getSimpleName(double[].class)); + + // Arrays of arrays of ... + assertEquals("String[][]", ClassUtils.getSimpleName(String[][].class)); + assertEquals("String[][][]", ClassUtils.getSimpleName(String[][][].class)); + assertEquals("String[][][][]", ClassUtils.getSimpleName(String[][][][].class)); + + // On-the-fly types + class Named extends Object {} + assertEquals("", ClassUtils.getSimpleName(new Object(){}.getClass())); + assertEquals("Named", ClassUtils.getSimpleName(Named.class)); + } + + public void test_getSimpleName_Object() { + assertEquals("ClassUtils", ClassUtils.getSimpleName(new ClassUtils(), "")); + assertEquals("Inner", ClassUtils.getSimpleName(new Inner(), "")); + assertEquals("String", ClassUtils.getSimpleName("hello", "")); + assertEquals("", ClassUtils.getSimpleName(null, "")); + } + + // ------------------------------------------------------------------------- + public void test_getPackageName_Object() { + assertEquals("org.apache.commons.lang3", ClassUtils.getPackageName(new ClassUtils(), "")); + assertEquals("org.apache.commons.lang3", ClassUtils.getPackageName(new Inner(), "")); + assertEquals("", ClassUtils.getPackageName(null, "")); + } + + public void test_getPackageName_Class() { + assertEquals("java.lang", ClassUtils.getPackageName(String.class)); + assertEquals("java.util", ClassUtils.getPackageName(Map.Entry.class)); + assertEquals("", ClassUtils.getPackageName((Class)null)); + + // LANG-535 + assertEquals("java.lang", ClassUtils.getPackageName(String[].class)); + + // Primitive Arrays + assertEquals("", ClassUtils.getPackageName(boolean[].class)); + assertEquals("", ClassUtils.getPackageName(byte[].class)); + assertEquals("", ClassUtils.getPackageName(char[].class)); + assertEquals("", ClassUtils.getPackageName(short[].class)); + assertEquals("", ClassUtils.getPackageName(int[].class)); + assertEquals("", ClassUtils.getPackageName(long[].class)); + assertEquals("", ClassUtils.getPackageName(float[].class)); + assertEquals("", ClassUtils.getPackageName(double[].class)); + + // Arrays of arrays of ... + assertEquals("java.lang", ClassUtils.getPackageName(String[][].class)); + assertEquals("java.lang", ClassUtils.getPackageName(String[][][].class)); + assertEquals("java.lang", ClassUtils.getPackageName(String[][][][].class)); + + // On-the-fly types + class Named extends Object {} + assertEquals("org.apache.commons.lang3", ClassUtils.getPackageName(new Object(){}.getClass())); + assertEquals("org.apache.commons.lang3", ClassUtils.getPackageName(Named.class)); + } + + public void test_getPackageName_String() { + assertEquals("org.apache.commons.lang3", ClassUtils.getPackageName(ClassUtils.class.getName())); + assertEquals("java.util", ClassUtils.getPackageName(Map.Entry.class.getName())); + assertEquals("", ClassUtils.getPackageName((String)null)); + assertEquals("", ClassUtils.getPackageName("")); + } + + // ------------------------------------------------------------------------- + public void test_getAllSuperclasses_Class() { + List list = ClassUtils.getAllSuperclasses(CY.class); + assertEquals(2, list.size()); + assertEquals(CX.class, list.get(0)); + assertEquals(Object.class, list.get(1)); + + assertEquals(null, ClassUtils.getAllSuperclasses(null)); + } + + public void test_getAllInterfaces_Class() { + List list = ClassUtils.getAllInterfaces(CY.class); + assertEquals(6, list.size()); + assertEquals(IB.class, list.get(0)); + assertEquals(IC.class, list.get(1)); + assertEquals(ID.class, list.get(2)); + assertEquals(IE.class, list.get(3)); + assertEquals(IF.class, list.get(4)); + assertEquals(IA.class, list.get(5)); + + assertEquals(null, ClassUtils.getAllInterfaces(null)); + } + + private static interface IA { + } + private static interface IB { + } + private static interface IC extends ID, IE { + } + private static interface ID { + } + private static interface IE extends IF { + } + private static interface IF { + } + private static class CX implements IB, IA, IE { + } + private static class CY extends CX implements IB, IC { + } + + // ------------------------------------------------------------------------- + public void test_convertClassNamesToClasses_List() { + List list = new ArrayList(); + List> result = ClassUtils.convertClassNamesToClasses(list); + assertEquals(0, result.size()); + + list.add("java.lang.String"); + list.add("java.lang.xxx"); + list.add("java.lang.Object"); + result = ClassUtils.convertClassNamesToClasses(list); + assertEquals(3, result.size()); + assertEquals(String.class, result.get(0)); + assertEquals(null, result.get(1)); + assertEquals(Object.class, result.get(2)); + + @SuppressWarnings("unchecked") // test what happens when non-generic code adds wrong type of element + List olist = (List)(List)list; + olist.add(new Object()); + try { + ClassUtils.convertClassNamesToClasses(list); + fail("Should not have been able to convert list"); + } catch (ClassCastException expected) {} + assertEquals(null, ClassUtils.convertClassNamesToClasses(null)); + } + + public void test_convertClassesToClassNames_List() { + List> list = new ArrayList>(); + List result = ClassUtils.convertClassesToClassNames(list); + assertEquals(0, result.size()); + + list.add(String.class); + list.add(null); + list.add(Object.class); + result = ClassUtils.convertClassesToClassNames(list); + assertEquals(3, result.size()); + assertEquals("java.lang.String", result.get(0)); + assertEquals(null, result.get(1)); + assertEquals("java.lang.Object", result.get(2)); + + @SuppressWarnings("unchecked") // test what happens when non-generic code adds wrong type of element + List olist = (List)(List)list; + olist.add(new Object()); + try { + ClassUtils.convertClassesToClassNames(list); + fail("Should not have been able to convert list"); + } catch (ClassCastException expected) {} + assertEquals(null, ClassUtils.convertClassesToClassNames(null)); + } + + // ------------------------------------------------------------------------- + public void test_isInnerClass_Class() { + assertEquals(true, ClassUtils.isInnerClass(Inner.class)); + assertEquals(true, ClassUtils.isInnerClass(Map.Entry.class)); + assertEquals(true, ClassUtils.isInnerClass(new Cloneable() { + }.getClass())); + assertEquals(false, ClassUtils.isInnerClass(this.getClass())); + assertEquals(false, ClassUtils.isInnerClass(String.class)); + assertEquals(false, ClassUtils.isInnerClass(null)); + } + + // ------------------------------------------------------------------------- + public void test_isAssignable_ClassArray_ClassArray() throws Exception { + Class[] array2 = new Class[] {Object.class, Object.class}; + Class[] array1 = new Class[] {Object.class}; + Class[] array1s = new Class[] {String.class}; + Class[] array0 = new Class[] {}; + Class[] arrayPrimitives = { Integer.TYPE, Boolean.TYPE }; + Class[] arrayWrappers = { Integer.class, Boolean.class }; + + assertFalse(ClassUtils.isAssignable(array1, array2)); + assertFalse(ClassUtils.isAssignable(null, array2)); + assertTrue(ClassUtils.isAssignable(null, array0)); + assertTrue(ClassUtils.isAssignable(array0, array0)); +// assertTrue(ClassUtils.isAssignable(array0, null)); + assertTrue(ClassUtils.isAssignable(array0, (Class[]) null)); // explicit cast to avoid warning + assertTrue(ClassUtils.isAssignable((Class[]) null, (Class[]) null)); + + assertFalse(ClassUtils.isAssignable(array1, array1s)); + assertTrue(ClassUtils.isAssignable(array1s, array1s)); + assertTrue(ClassUtils.isAssignable(array1s, array1)); + + boolean autoboxing = SystemUtils.isJavaVersionAtLeast(JAVA_1_5); + + assertEquals(autoboxing, ClassUtils.isAssignable(arrayPrimitives, arrayWrappers)); + assertEquals(autoboxing, ClassUtils.isAssignable(arrayWrappers, arrayPrimitives)); + assertFalse(ClassUtils.isAssignable(arrayPrimitives, array1)); + assertFalse(ClassUtils.isAssignable(arrayWrappers, array1)); + assertEquals(autoboxing, ClassUtils.isAssignable(arrayPrimitives, array2)); + assertTrue(ClassUtils.isAssignable(arrayWrappers, array2)); + } + + public void test_isAssignable_ClassArray_ClassArray_Autoboxing() throws Exception { + Class[] array2 = new Class[] {Object.class, Object.class}; + Class[] array1 = new Class[] {Object.class}; + Class[] array1s = new Class[] {String.class}; + Class[] array0 = new Class[] {}; + Class[] arrayPrimitives = { Integer.TYPE, Boolean.TYPE }; + Class[] arrayWrappers = { Integer.class, Boolean.class }; + + assertFalse(ClassUtils.isAssignable(array1, array2, true)); + assertFalse(ClassUtils.isAssignable(null, array2, true)); + assertTrue(ClassUtils.isAssignable(null, array0, true)); + assertTrue(ClassUtils.isAssignable(array0, array0, true)); + assertTrue(ClassUtils.isAssignable(array0, null, true)); + assertTrue(ClassUtils.isAssignable((Class[]) null, (Class[]) null, true)); + + assertFalse(ClassUtils.isAssignable(array1, array1s, true)); + assertTrue(ClassUtils.isAssignable(array1s, array1s, true)); + assertTrue(ClassUtils.isAssignable(array1s, array1, true)); + + assertTrue(ClassUtils.isAssignable(arrayPrimitives, arrayWrappers, true)); + assertTrue(ClassUtils.isAssignable(arrayWrappers, arrayPrimitives, true)); + assertFalse(ClassUtils.isAssignable(arrayPrimitives, array1, true)); + assertFalse(ClassUtils.isAssignable(arrayWrappers, array1, true)); + assertTrue(ClassUtils.isAssignable(arrayPrimitives, array2, true)); + assertTrue(ClassUtils.isAssignable(arrayWrappers, array2, true)); + } + + public void test_isAssignable_ClassArray_ClassArray_NoAutoboxing() throws Exception { + Class[] array2 = new Class[] {Object.class, Object.class}; + Class[] array1 = new Class[] {Object.class}; + Class[] array1s = new Class[] {String.class}; + Class[] array0 = new Class[] {}; + Class[] arrayPrimitives = { Integer.TYPE, Boolean.TYPE }; + Class[] arrayWrappers = { Integer.class, Boolean.class }; + + assertFalse(ClassUtils.isAssignable(array1, array2, false)); + assertFalse(ClassUtils.isAssignable(null, array2, false)); + assertTrue(ClassUtils.isAssignable(null, array0, false)); + assertTrue(ClassUtils.isAssignable(array0, array0, false)); + assertTrue(ClassUtils.isAssignable(array0, null, false)); + assertTrue(ClassUtils.isAssignable((Class[]) null, (Class[]) null, false)); + + assertFalse(ClassUtils.isAssignable(array1, array1s, false)); + assertTrue(ClassUtils.isAssignable(array1s, array1s, false)); + assertTrue(ClassUtils.isAssignable(array1s, array1, false)); + + assertFalse(ClassUtils.isAssignable(arrayPrimitives, arrayWrappers, false)); + assertFalse(ClassUtils.isAssignable(arrayWrappers, arrayPrimitives, false)); + assertFalse(ClassUtils.isAssignable(arrayPrimitives, array1, false)); + assertFalse(ClassUtils.isAssignable(arrayWrappers, array1, false)); + assertTrue(ClassUtils.isAssignable(arrayWrappers, array2, false)); + assertFalse(ClassUtils.isAssignable(arrayPrimitives, array2, false)); + } + + public void test_isAssignable() throws Exception { + assertFalse(ClassUtils.isAssignable((Class) null, null)); + assertFalse(ClassUtils.isAssignable(String.class, null)); + + assertTrue(ClassUtils.isAssignable(null, Object.class)); + assertTrue(ClassUtils.isAssignable(null, Integer.class)); + assertFalse(ClassUtils.isAssignable(null, Integer.TYPE)); + assertTrue(ClassUtils.isAssignable(String.class, Object.class)); + assertTrue(ClassUtils.isAssignable(String.class, String.class)); + assertFalse(ClassUtils.isAssignable(Object.class, String.class)); + + boolean autoboxing = SystemUtils.isJavaVersionAtLeast(JAVA_1_5); + + assertEquals(autoboxing, ClassUtils.isAssignable(Integer.TYPE, Integer.class)); + assertEquals(autoboxing, ClassUtils.isAssignable(Integer.TYPE, Object.class)); + assertEquals(autoboxing, ClassUtils.isAssignable(Integer.class, Integer.TYPE)); + assertEquals(autoboxing, ClassUtils.isAssignable(Integer.class, Object.class)); + assertTrue(ClassUtils.isAssignable(Integer.TYPE, Integer.TYPE)); + assertTrue(ClassUtils.isAssignable(Integer.class, Integer.class)); + assertEquals(autoboxing, ClassUtils.isAssignable(Boolean.TYPE, Boolean.class)); + assertEquals(autoboxing, ClassUtils.isAssignable(Boolean.TYPE, Object.class)); + assertEquals(autoboxing, ClassUtils.isAssignable(Boolean.class, Boolean.TYPE)); + assertEquals(autoboxing, ClassUtils.isAssignable(Boolean.class, Object.class)); + assertTrue(ClassUtils.isAssignable(Boolean.TYPE, Boolean.TYPE)); + assertTrue(ClassUtils.isAssignable(Boolean.class, Boolean.class)); + } + + public void test_isAssignable_Autoboxing() throws Exception { + assertFalse(ClassUtils.isAssignable((Class) null, null, true)); + assertFalse(ClassUtils.isAssignable(String.class, null, true)); + + assertTrue(ClassUtils.isAssignable(null, Object.class, true)); + assertTrue(ClassUtils.isAssignable(null, Integer.class, true)); + assertFalse(ClassUtils.isAssignable(null, Integer.TYPE, true)); + assertTrue(ClassUtils.isAssignable(String.class, Object.class, true)); + assertTrue(ClassUtils.isAssignable(String.class, String.class, true)); + assertFalse(ClassUtils.isAssignable(Object.class, String.class, true)); + assertTrue(ClassUtils.isAssignable(Integer.TYPE, Integer.class, true)); + assertTrue(ClassUtils.isAssignable(Integer.TYPE, Object.class, true)); + assertTrue(ClassUtils.isAssignable(Integer.class, Integer.TYPE, true)); + assertTrue(ClassUtils.isAssignable(Integer.class, Object.class, true)); + assertTrue(ClassUtils.isAssignable(Integer.TYPE, Integer.TYPE, true)); + assertTrue(ClassUtils.isAssignable(Integer.class, Integer.class, true)); + assertTrue(ClassUtils.isAssignable(Boolean.TYPE, Boolean.class, true)); + assertTrue(ClassUtils.isAssignable(Boolean.class, Boolean.TYPE, true)); + assertTrue(ClassUtils.isAssignable(Boolean.class, Object.class, true)); + assertTrue(ClassUtils.isAssignable(Boolean.TYPE, Boolean.TYPE, true)); + assertTrue(ClassUtils.isAssignable(Boolean.class, Boolean.class, true)); + } + + public void test_isAssignable_NoAutoboxing() throws Exception { + assertFalse(ClassUtils.isAssignable((Class) null, null, false)); + assertFalse(ClassUtils.isAssignable(String.class, null, false)); + + assertTrue(ClassUtils.isAssignable(null, Object.class, false)); + assertTrue(ClassUtils.isAssignable(null, Integer.class, false)); + assertFalse(ClassUtils.isAssignable(null, Integer.TYPE, false)); + assertTrue(ClassUtils.isAssignable(String.class, Object.class, false)); + assertTrue(ClassUtils.isAssignable(String.class, String.class, false)); + assertFalse(ClassUtils.isAssignable(Object.class, String.class, false)); + assertFalse(ClassUtils.isAssignable(Integer.TYPE, Integer.class, false)); + assertFalse(ClassUtils.isAssignable(Integer.TYPE, Object.class, false)); + assertFalse(ClassUtils.isAssignable(Integer.class, Integer.TYPE, false)); + assertTrue(ClassUtils.isAssignable(Integer.TYPE, Integer.TYPE, false)); + assertTrue(ClassUtils.isAssignable(Integer.class, Integer.class, false)); + assertFalse(ClassUtils.isAssignable(Boolean.TYPE, Boolean.class, false)); + assertFalse(ClassUtils.isAssignable(Boolean.TYPE, Object.class, false)); + assertFalse(ClassUtils.isAssignable(Boolean.class, Boolean.TYPE, false)); + assertTrue(ClassUtils.isAssignable(Boolean.class, Object.class, false)); + assertTrue(ClassUtils.isAssignable(Boolean.TYPE, Boolean.TYPE, false)); + assertTrue(ClassUtils.isAssignable(Boolean.class, Boolean.class, false)); + } + + public void test_isAssignable_Widening() throws Exception { + // test byte conversions + assertFalse("byte -> char", ClassUtils.isAssignable(Byte.TYPE, Character.TYPE)); + assertTrue("byte -> byte", ClassUtils.isAssignable(Byte.TYPE, Byte.TYPE)); + assertTrue("byte -> short", ClassUtils.isAssignable(Byte.TYPE, Short.TYPE)); + assertTrue("byte -> int", ClassUtils.isAssignable(Byte.TYPE, Integer.TYPE)); + assertTrue("byte -> long", ClassUtils.isAssignable(Byte.TYPE, Long.TYPE)); + assertTrue("byte -> float", ClassUtils.isAssignable(Byte.TYPE, Float.TYPE)); + assertTrue("byte -> double", ClassUtils.isAssignable(Byte.TYPE, Double.TYPE)); + assertFalse("byte -> boolean", ClassUtils.isAssignable(Byte.TYPE, Boolean.TYPE)); + + // test short conversions + assertFalse("short -> char", ClassUtils.isAssignable(Short.TYPE, Character.TYPE)); + assertFalse("short -> byte", ClassUtils.isAssignable(Short.TYPE, Byte.TYPE)); + assertTrue("short -> short", ClassUtils.isAssignable(Short.TYPE, Short.TYPE)); + assertTrue("short -> int", ClassUtils.isAssignable(Short.TYPE, Integer.TYPE)); + assertTrue("short -> long", ClassUtils.isAssignable(Short.TYPE, Long.TYPE)); + assertTrue("short -> float", ClassUtils.isAssignable(Short.TYPE, Float.TYPE)); + assertTrue("short -> double", ClassUtils.isAssignable(Short.TYPE, Double.TYPE)); + assertFalse("short -> boolean", ClassUtils.isAssignable(Short.TYPE, Boolean.TYPE)); + + // test char conversions + assertTrue("char -> char", ClassUtils.isAssignable(Character.TYPE, Character.TYPE)); + assertFalse("char -> byte", ClassUtils.isAssignable(Character.TYPE, Byte.TYPE)); + assertFalse("char -> short", ClassUtils.isAssignable(Character.TYPE, Short.TYPE)); + assertTrue("char -> int", ClassUtils.isAssignable(Character.TYPE, Integer.TYPE)); + assertTrue("char -> long", ClassUtils.isAssignable(Character.TYPE, Long.TYPE)); + assertTrue("char -> float", ClassUtils.isAssignable(Character.TYPE, Float.TYPE)); + assertTrue("char -> double", ClassUtils.isAssignable(Character.TYPE, Double.TYPE)); + assertFalse("char -> boolean", ClassUtils.isAssignable(Character.TYPE, Boolean.TYPE)); + + // test int conversions + assertFalse("int -> char", ClassUtils.isAssignable(Integer.TYPE, Character.TYPE)); + assertFalse("int -> byte", ClassUtils.isAssignable(Integer.TYPE, Byte.TYPE)); + assertFalse("int -> short", ClassUtils.isAssignable(Integer.TYPE, Short.TYPE)); + assertTrue("int -> int", ClassUtils.isAssignable(Integer.TYPE, Integer.TYPE)); + assertTrue("int -> long", ClassUtils.isAssignable(Integer.TYPE, Long.TYPE)); + assertTrue("int -> float", ClassUtils.isAssignable(Integer.TYPE, Float.TYPE)); + assertTrue("int -> double", ClassUtils.isAssignable(Integer.TYPE, Double.TYPE)); + assertFalse("int -> boolean", ClassUtils.isAssignable(Integer.TYPE, Boolean.TYPE)); + + // test long conversions + assertFalse("long -> char", ClassUtils.isAssignable(Long.TYPE, Character.TYPE)); + assertFalse("long -> byte", ClassUtils.isAssignable(Long.TYPE, Byte.TYPE)); + assertFalse("long -> short", ClassUtils.isAssignable(Long.TYPE, Short.TYPE)); + assertFalse("long -> int", ClassUtils.isAssignable(Long.TYPE, Integer.TYPE)); + assertTrue("long -> long", ClassUtils.isAssignable(Long.TYPE, Long.TYPE)); + assertTrue("long -> float", ClassUtils.isAssignable(Long.TYPE, Float.TYPE)); + assertTrue("long -> double", ClassUtils.isAssignable(Long.TYPE, Double.TYPE)); + assertFalse("long -> boolean", ClassUtils.isAssignable(Long.TYPE, Boolean.TYPE)); + + // test float conversions + assertFalse("float -> char", ClassUtils.isAssignable(Float.TYPE, Character.TYPE)); + assertFalse("float -> byte", ClassUtils.isAssignable(Float.TYPE, Byte.TYPE)); + assertFalse("float -> short", ClassUtils.isAssignable(Float.TYPE, Short.TYPE)); + assertFalse("float -> int", ClassUtils.isAssignable(Float.TYPE, Integer.TYPE)); + assertFalse("float -> long", ClassUtils.isAssignable(Float.TYPE, Long.TYPE)); + assertTrue("float -> float", ClassUtils.isAssignable(Float.TYPE, Float.TYPE)); + assertTrue("float -> double", ClassUtils.isAssignable(Float.TYPE, Double.TYPE)); + assertFalse("float -> boolean", ClassUtils.isAssignable(Float.TYPE, Boolean.TYPE)); + + // test double conversions + assertFalse("double -> char", ClassUtils.isAssignable(Double.TYPE, Character.TYPE)); + assertFalse("double -> byte", ClassUtils.isAssignable(Double.TYPE, Byte.TYPE)); + assertFalse("double -> short", ClassUtils.isAssignable(Double.TYPE, Short.TYPE)); + assertFalse("double -> int", ClassUtils.isAssignable(Double.TYPE, Integer.TYPE)); + assertFalse("double -> long", ClassUtils.isAssignable(Double.TYPE, Long.TYPE)); + assertFalse("double -> float", ClassUtils.isAssignable(Double.TYPE, Float.TYPE)); + assertTrue("double -> double", ClassUtils.isAssignable(Double.TYPE, Double.TYPE)); + assertFalse("double -> boolean", ClassUtils.isAssignable(Double.TYPE, Boolean.TYPE)); + + // test boolean conversions + assertFalse("boolean -> char", ClassUtils.isAssignable(Boolean.TYPE, Character.TYPE)); + assertFalse("boolean -> byte", ClassUtils.isAssignable(Boolean.TYPE, Byte.TYPE)); + assertFalse("boolean -> short", ClassUtils.isAssignable(Boolean.TYPE, Short.TYPE)); + assertFalse("boolean -> int", ClassUtils.isAssignable(Boolean.TYPE, Integer.TYPE)); + assertFalse("boolean -> long", ClassUtils.isAssignable(Boolean.TYPE, Long.TYPE)); + assertFalse("boolean -> float", ClassUtils.isAssignable(Boolean.TYPE, Float.TYPE)); + assertFalse("boolean -> double", ClassUtils.isAssignable(Boolean.TYPE, Double.TYPE)); + assertTrue("boolean -> boolean", ClassUtils.isAssignable(Boolean.TYPE, Boolean.TYPE)); + } + + public void test_isAssignable_DefaultUnboxing_Widening() throws Exception { + boolean autoboxing = SystemUtils.isJavaVersionAtLeast(JAVA_1_5); + + // test byte conversions + assertFalse("byte -> char", ClassUtils.isAssignable(Byte.class, Character.TYPE)); + assertEquals("byte -> byte", autoboxing, ClassUtils.isAssignable(Byte.class, Byte.TYPE)); + assertEquals("byte -> short", autoboxing, ClassUtils.isAssignable(Byte.class, Short.TYPE)); + assertEquals("byte -> int", autoboxing, ClassUtils.isAssignable(Byte.class, Integer.TYPE)); + assertEquals("byte -> long", autoboxing, ClassUtils.isAssignable(Byte.class, Long.TYPE)); + assertEquals("byte -> float", autoboxing, ClassUtils.isAssignable(Byte.class, Float.TYPE)); + assertEquals("byte -> double", autoboxing, ClassUtils.isAssignable(Byte.class, Double.TYPE)); + assertFalse("byte -> boolean", ClassUtils.isAssignable(Byte.class, Boolean.TYPE)); + + // test short conversions + assertFalse("short -> char", ClassUtils.isAssignable(Short.class, Character.TYPE)); + assertFalse("short -> byte", ClassUtils.isAssignable(Short.class, Byte.TYPE)); + assertEquals("short -> short", autoboxing, ClassUtils.isAssignable(Short.class, Short.TYPE)); + assertEquals("short -> int", autoboxing, ClassUtils.isAssignable(Short.class, Integer.TYPE)); + assertEquals("short -> long", autoboxing, ClassUtils.isAssignable(Short.class, Long.TYPE)); + assertEquals("short -> float", autoboxing, ClassUtils.isAssignable(Short.class, Float.TYPE)); + assertEquals("short -> double", autoboxing, ClassUtils.isAssignable(Short.class, Double.TYPE)); + assertFalse("short -> boolean", ClassUtils.isAssignable(Short.class, Boolean.TYPE)); + + // test char conversions + assertEquals("char -> char", autoboxing, ClassUtils.isAssignable(Character.class, Character.TYPE)); + assertFalse("char -> byte", ClassUtils.isAssignable(Character.class, Byte.TYPE)); + assertFalse("char -> short", ClassUtils.isAssignable(Character.class, Short.TYPE)); + assertEquals("char -> int", autoboxing, ClassUtils.isAssignable(Character.class, Integer.TYPE)); + assertEquals("char -> long", autoboxing, ClassUtils.isAssignable(Character.class, Long.TYPE)); + assertEquals("char -> float", autoboxing, ClassUtils.isAssignable(Character.class, Float.TYPE)); + assertEquals("char -> double", autoboxing, ClassUtils.isAssignable(Character.class, Double.TYPE)); + assertFalse("char -> boolean", ClassUtils.isAssignable(Character.class, Boolean.TYPE)); + + // test int conversions + assertFalse("int -> char", ClassUtils.isAssignable(Integer.class, Character.TYPE)); + assertFalse("int -> byte", ClassUtils.isAssignable(Integer.class, Byte.TYPE)); + assertFalse("int -> short", ClassUtils.isAssignable(Integer.class, Short.TYPE)); + assertEquals("int -> int", autoboxing, ClassUtils.isAssignable(Integer.class, Integer.TYPE)); + assertEquals("int -> long", autoboxing, ClassUtils.isAssignable(Integer.class, Long.TYPE)); + assertEquals("int -> float", autoboxing, ClassUtils.isAssignable(Integer.class, Float.TYPE)); + assertEquals("int -> double", autoboxing, ClassUtils.isAssignable(Integer.class, Double.TYPE)); + assertFalse("int -> boolean", ClassUtils.isAssignable(Integer.class, Boolean.TYPE)); + + // test long conversions + assertFalse("long -> char", ClassUtils.isAssignable(Long.class, Character.TYPE)); + assertFalse("long -> byte", ClassUtils.isAssignable(Long.class, Byte.TYPE)); + assertFalse("long -> short", ClassUtils.isAssignable(Long.class, Short.TYPE)); + assertFalse("long -> int", ClassUtils.isAssignable(Long.class, Integer.TYPE)); + assertEquals("long -> long", autoboxing, ClassUtils.isAssignable(Long.class, Long.TYPE)); + assertEquals("long -> float", autoboxing, ClassUtils.isAssignable(Long.class, Float.TYPE)); + assertEquals("long -> double", autoboxing, ClassUtils.isAssignable(Long.class, Double.TYPE)); + assertFalse("long -> boolean", ClassUtils.isAssignable(Long.class, Boolean.TYPE)); + + // test float conversions + assertFalse("float -> char", ClassUtils.isAssignable(Float.class, Character.TYPE)); + assertFalse("float -> byte", ClassUtils.isAssignable(Float.class, Byte.TYPE)); + assertFalse("float -> short", ClassUtils.isAssignable(Float.class, Short.TYPE)); + assertFalse("float -> int", ClassUtils.isAssignable(Float.class, Integer.TYPE)); + assertFalse("float -> long", ClassUtils.isAssignable(Float.class, Long.TYPE)); + assertEquals("float -> float", autoboxing, ClassUtils.isAssignable(Float.class, Float.TYPE)); + assertEquals("float -> double", autoboxing, ClassUtils.isAssignable(Float.class, Double.TYPE)); + assertFalse("float -> boolean", ClassUtils.isAssignable(Float.class, Boolean.TYPE)); + + // test double conversions + assertFalse("double -> char", ClassUtils.isAssignable(Double.class, Character.TYPE)); + assertFalse("double -> byte", ClassUtils.isAssignable(Double.class, Byte.TYPE)); + assertFalse("double -> short", ClassUtils.isAssignable(Double.class, Short.TYPE)); + assertFalse("double -> int", ClassUtils.isAssignable(Double.class, Integer.TYPE)); + assertFalse("double -> long", ClassUtils.isAssignable(Double.class, Long.TYPE)); + assertFalse("double -> float", ClassUtils.isAssignable(Double.class, Float.TYPE)); + assertEquals("double -> double", autoboxing, ClassUtils.isAssignable(Double.class, Double.TYPE)); + assertFalse("double -> boolean", ClassUtils.isAssignable(Double.class, Boolean.TYPE)); + + // test boolean conversions + assertFalse("boolean -> char", ClassUtils.isAssignable(Boolean.class, Character.TYPE)); + assertFalse("boolean -> byte", ClassUtils.isAssignable(Boolean.class, Byte.TYPE)); + assertFalse("boolean -> short", ClassUtils.isAssignable(Boolean.class, Short.TYPE)); + assertFalse("boolean -> int", ClassUtils.isAssignable(Boolean.class, Integer.TYPE)); + assertFalse("boolean -> long", ClassUtils.isAssignable(Boolean.class, Long.TYPE)); + assertFalse("boolean -> float", ClassUtils.isAssignable(Boolean.class, Float.TYPE)); + assertFalse("boolean -> double", ClassUtils.isAssignable(Boolean.class, Double.TYPE)); + assertEquals("boolean -> boolean", autoboxing, ClassUtils.isAssignable(Boolean.class, Boolean.TYPE)); + } + + public void test_isAssignable_Unboxing_Widening() throws Exception { + // test byte conversions + assertFalse("byte -> char", ClassUtils.isAssignable(Byte.class, Character.TYPE, true)); + assertTrue("byte -> byte", ClassUtils.isAssignable(Byte.class, Byte.TYPE, true)); + assertTrue("byte -> short", ClassUtils.isAssignable(Byte.class, Short.TYPE, true)); + assertTrue("byte -> int", ClassUtils.isAssignable(Byte.class, Integer.TYPE, true)); + assertTrue("byte -> long", ClassUtils.isAssignable(Byte.class, Long.TYPE, true)); + assertTrue("byte -> float", ClassUtils.isAssignable(Byte.class, Float.TYPE, true)); + assertTrue("byte -> double", ClassUtils.isAssignable(Byte.class, Double.TYPE, true)); + assertFalse("byte -> boolean", ClassUtils.isAssignable(Byte.class, Boolean.TYPE, true)); + + // test short conversions + assertFalse("short -> char", ClassUtils.isAssignable(Short.class, Character.TYPE, true)); + assertFalse("short -> byte", ClassUtils.isAssignable(Short.class, Byte.TYPE, true)); + assertTrue("short -> short", ClassUtils.isAssignable(Short.class, Short.TYPE, true)); + assertTrue("short -> int", ClassUtils.isAssignable(Short.class, Integer.TYPE, true)); + assertTrue("short -> long", ClassUtils.isAssignable(Short.class, Long.TYPE, true)); + assertTrue("short -> float", ClassUtils.isAssignable(Short.class, Float.TYPE, true)); + assertTrue("short -> double", ClassUtils.isAssignable(Short.class, Double.TYPE, true)); + assertFalse("short -> boolean", ClassUtils.isAssignable(Short.class, Boolean.TYPE, true)); + + // test char conversions + assertTrue("char -> char", ClassUtils.isAssignable(Character.class, Character.TYPE, true)); + assertFalse("char -> byte", ClassUtils.isAssignable(Character.class, Byte.TYPE, true)); + assertFalse("char -> short", ClassUtils.isAssignable(Character.class, Short.TYPE, true)); + assertTrue("char -> int", ClassUtils.isAssignable(Character.class, Integer.TYPE, true)); + assertTrue("char -> long", ClassUtils.isAssignable(Character.class, Long.TYPE, true)); + assertTrue("char -> float", ClassUtils.isAssignable(Character.class, Float.TYPE, true)); + assertTrue("char -> double", ClassUtils.isAssignable(Character.class, Double.TYPE, true)); + assertFalse("char -> boolean", ClassUtils.isAssignable(Character.class, Boolean.TYPE, true)); + + // test int conversions + assertFalse("int -> char", ClassUtils.isAssignable(Integer.class, Character.TYPE, true)); + assertFalse("int -> byte", ClassUtils.isAssignable(Integer.class, Byte.TYPE, true)); + assertFalse("int -> short", ClassUtils.isAssignable(Integer.class, Short.TYPE, true)); + assertTrue("int -> int", ClassUtils.isAssignable(Integer.class, Integer.TYPE, true)); + assertTrue("int -> long", ClassUtils.isAssignable(Integer.class, Long.TYPE, true)); + assertTrue("int -> float", ClassUtils.isAssignable(Integer.class, Float.TYPE, true)); + assertTrue("int -> double", ClassUtils.isAssignable(Integer.class, Double.TYPE, true)); + assertFalse("int -> boolean", ClassUtils.isAssignable(Integer.class, Boolean.TYPE, true)); + + // test long conversions + assertFalse("long -> char", ClassUtils.isAssignable(Long.class, Character.TYPE, true)); + assertFalse("long -> byte", ClassUtils.isAssignable(Long.class, Byte.TYPE, true)); + assertFalse("long -> short", ClassUtils.isAssignable(Long.class, Short.TYPE, true)); + assertFalse("long -> int", ClassUtils.isAssignable(Long.class, Integer.TYPE, true)); + assertTrue("long -> long", ClassUtils.isAssignable(Long.class, Long.TYPE, true)); + assertTrue("long -> float", ClassUtils.isAssignable(Long.class, Float.TYPE, true)); + assertTrue("long -> double", ClassUtils.isAssignable(Long.class, Double.TYPE, true)); + assertFalse("long -> boolean", ClassUtils.isAssignable(Long.class, Boolean.TYPE, true)); + + // test float conversions + assertFalse("float -> char", ClassUtils.isAssignable(Float.class, Character.TYPE, true)); + assertFalse("float -> byte", ClassUtils.isAssignable(Float.class, Byte.TYPE, true)); + assertFalse("float -> short", ClassUtils.isAssignable(Float.class, Short.TYPE, true)); + assertFalse("float -> int", ClassUtils.isAssignable(Float.class, Integer.TYPE, true)); + assertFalse("float -> long", ClassUtils.isAssignable(Float.class, Long.TYPE, true)); + assertTrue("float -> float", ClassUtils.isAssignable(Float.class, Float.TYPE, true)); + assertTrue("float -> double", ClassUtils.isAssignable(Float.class, Double.TYPE, true)); + assertFalse("float -> boolean", ClassUtils.isAssignable(Float.class, Boolean.TYPE, true)); + + // test double conversions + assertFalse("double -> char", ClassUtils.isAssignable(Double.class, Character.TYPE, true)); + assertFalse("double -> byte", ClassUtils.isAssignable(Double.class, Byte.TYPE, true)); + assertFalse("double -> short", ClassUtils.isAssignable(Double.class, Short.TYPE, true)); + assertFalse("double -> int", ClassUtils.isAssignable(Double.class, Integer.TYPE, true)); + assertFalse("double -> long", ClassUtils.isAssignable(Double.class, Long.TYPE, true)); + assertFalse("double -> float", ClassUtils.isAssignable(Double.class, Float.TYPE, true)); + assertTrue("double -> double", ClassUtils.isAssignable(Double.class, Double.TYPE, true)); + assertFalse("double -> boolean", ClassUtils.isAssignable(Double.class, Boolean.TYPE, true)); + + // test boolean conversions + assertFalse("boolean -> char", ClassUtils.isAssignable(Boolean.class, Character.TYPE, true)); + assertFalse("boolean -> byte", ClassUtils.isAssignable(Boolean.class, Byte.TYPE, true)); + assertFalse("boolean -> short", ClassUtils.isAssignable(Boolean.class, Short.TYPE, true)); + assertFalse("boolean -> int", ClassUtils.isAssignable(Boolean.class, Integer.TYPE, true)); + assertFalse("boolean -> long", ClassUtils.isAssignable(Boolean.class, Long.TYPE, true)); + assertFalse("boolean -> float", ClassUtils.isAssignable(Boolean.class, Float.TYPE, true)); + assertFalse("boolean -> double", ClassUtils.isAssignable(Boolean.class, Double.TYPE, true)); + assertTrue("boolean -> boolean", ClassUtils.isAssignable(Boolean.class, Boolean.TYPE, true)); + } + + public void testIsPrimitiveOrWrapper() { + + // test primitive wrapper classes + assertTrue("Boolean.class", ClassUtils.isPrimitiveOrWrapper(Boolean.class)); + assertTrue("Byte.class", ClassUtils.isPrimitiveOrWrapper(Byte.class)); + assertTrue("Character.class", ClassUtils.isPrimitiveOrWrapper(Character.class)); + assertTrue("Short.class", ClassUtils.isPrimitiveOrWrapper(Short.class)); + assertTrue("Integer.class", ClassUtils.isPrimitiveOrWrapper(Integer.class)); + assertTrue("Long.class", ClassUtils.isPrimitiveOrWrapper(Long.class)); + assertTrue("Double.class", ClassUtils.isPrimitiveOrWrapper(Double.class)); + assertTrue("Float.class", ClassUtils.isPrimitiveOrWrapper(Float.class)); + + // test primitive classes + assertTrue("boolean", ClassUtils.isPrimitiveOrWrapper(Boolean.TYPE)); + assertTrue("byte", ClassUtils.isPrimitiveOrWrapper(Byte.TYPE)); + assertTrue("char", ClassUtils.isPrimitiveOrWrapper(Character.TYPE)); + assertTrue("short", ClassUtils.isPrimitiveOrWrapper(Short.TYPE)); + assertTrue("int", ClassUtils.isPrimitiveOrWrapper(Integer.TYPE)); + assertTrue("long", ClassUtils.isPrimitiveOrWrapper(Long.TYPE)); + assertTrue("double", ClassUtils.isPrimitiveOrWrapper(Double.TYPE)); + assertTrue("float", ClassUtils.isPrimitiveOrWrapper(Float.TYPE)); + assertTrue("Void.TYPE", ClassUtils.isPrimitiveOrWrapper(Void.TYPE)); + + // others + assertFalse("null", ClassUtils.isPrimitiveOrWrapper(null)); + assertFalse("Void.class", ClassUtils.isPrimitiveOrWrapper(Void.class)); + assertFalse("String.class", ClassUtils.isPrimitiveOrWrapper(String.class)); + assertFalse("this.getClass()", ClassUtils.isPrimitiveOrWrapper(this.getClass())); + } + + public void testIsPrimitiveWrapper() { + + // test primitive wrapper classes + assertTrue("Boolean.class", ClassUtils.isPrimitiveWrapper(Boolean.class)); + assertTrue("Byte.class", ClassUtils.isPrimitiveWrapper(Byte.class)); + assertTrue("Character.class", ClassUtils.isPrimitiveWrapper(Character.class)); + assertTrue("Short.class", ClassUtils.isPrimitiveWrapper(Short.class)); + assertTrue("Integer.class", ClassUtils.isPrimitiveWrapper(Integer.class)); + assertTrue("Long.class", ClassUtils.isPrimitiveWrapper(Long.class)); + assertTrue("Double.class", ClassUtils.isPrimitiveWrapper(Double.class)); + assertTrue("Float.class", ClassUtils.isPrimitiveWrapper(Float.class)); + + // test primitive classes + assertFalse("boolean", ClassUtils.isPrimitiveWrapper(Boolean.TYPE)); + assertFalse("byte", ClassUtils.isPrimitiveWrapper(Byte.TYPE)); + assertFalse("char", ClassUtils.isPrimitiveWrapper(Character.TYPE)); + assertFalse("short", ClassUtils.isPrimitiveWrapper(Short.TYPE)); + assertFalse("int", ClassUtils.isPrimitiveWrapper(Integer.TYPE)); + assertFalse("long", ClassUtils.isPrimitiveWrapper(Long.TYPE)); + assertFalse("double", ClassUtils.isPrimitiveWrapper(Double.TYPE)); + assertFalse("float", ClassUtils.isPrimitiveWrapper(Float.TYPE)); + + // others + assertFalse("null", ClassUtils.isPrimitiveWrapper(null)); + assertFalse("Void.class", ClassUtils.isPrimitiveWrapper(Void.class)); + assertFalse("Void.TYPE", ClassUtils.isPrimitiveWrapper(Void.TYPE)); + assertFalse("String.class", ClassUtils.isPrimitiveWrapper(String.class)); + assertFalse("this.getClass()", ClassUtils.isPrimitiveWrapper(this.getClass())); + } + + public void testPrimitiveToWrapper() { + + // test primitive classes + assertEquals("boolean -> Boolean.class", + Boolean.class, ClassUtils.primitiveToWrapper(Boolean.TYPE)); + assertEquals("byte -> Byte.class", + Byte.class, ClassUtils.primitiveToWrapper(Byte.TYPE)); + assertEquals("char -> Character.class", + Character.class, ClassUtils.primitiveToWrapper(Character.TYPE)); + assertEquals("short -> Short.class", + Short.class, ClassUtils.primitiveToWrapper(Short.TYPE)); + assertEquals("int -> Integer.class", + Integer.class, ClassUtils.primitiveToWrapper(Integer.TYPE)); + assertEquals("long -> Long.class", + Long.class, ClassUtils.primitiveToWrapper(Long.TYPE)); + assertEquals("double -> Double.class", + Double.class, ClassUtils.primitiveToWrapper(Double.TYPE)); + assertEquals("float -> Float.class", + Float.class, ClassUtils.primitiveToWrapper(Float.TYPE)); + + // test a few other classes + assertEquals("String.class -> String.class", + String.class, ClassUtils.primitiveToWrapper(String.class)); + assertEquals("ClassUtils.class -> ClassUtils.class", + org.apache.commons.lang3.ClassUtils.class, + ClassUtils.primitiveToWrapper(org.apache.commons.lang3.ClassUtils.class)); + assertEquals("Void.TYPE -> Void.TYPE", + Void.TYPE, ClassUtils.primitiveToWrapper(Void.TYPE)); + + // test null + assertNull("null -> null", + ClassUtils.primitiveToWrapper(null)); + } + + public void testPrimitivesToWrappers() { + // test null +// assertNull("null -> null", ClassUtils.primitivesToWrappers(null)); // generates warning + assertNull("null -> null", ClassUtils.primitivesToWrappers((Class[]) null)); // equivalent cast to avoid warning + // Other possible casts for null + assertTrue("empty -> empty", Arrays.equals(ArrayUtils.EMPTY_CLASS_ARRAY, ClassUtils.primitivesToWrappers())); + Class[] castNull = ClassUtils.primitivesToWrappers((Class)null); // == new Class[]{null} + assertTrue("(Class)null -> [null]", Arrays.equals(new Class[]{null}, castNull)); + // test empty array is returned unchanged + // TODO this is not documented + assertEquals("empty -> empty", + ArrayUtils.EMPTY_CLASS_ARRAY, ClassUtils.primitivesToWrappers(ArrayUtils.EMPTY_CLASS_ARRAY)); + + // test an array of various classes + final Class[] primitives = new Class[] { + Boolean.TYPE, Byte.TYPE, Character.TYPE, Short.TYPE, + Integer.TYPE, Long.TYPE, Double.TYPE, Float.TYPE, + String.class, ClassUtils.class + }; + Class[] wrappers= ClassUtils.primitivesToWrappers(primitives); + + for (int i=0; i < primitives.length; i++) { + // test each returned wrapper + Class primitive = primitives[i]; + Class expectedWrapper = ClassUtils.primitiveToWrapper(primitive); + + assertEquals(primitive + " -> " + expectedWrapper, expectedWrapper, wrappers[i]); + } + + // test an array of no primitive classes + final Class[] noPrimitives = new Class[] { + String.class, ClassUtils.class, Void.TYPE + }; + // This used to return the exact same array, but no longer does. + assertNotSame("unmodified", noPrimitives, ClassUtils.primitivesToWrappers(noPrimitives)); + } + + public void testWrapperToPrimitive() { + // an array with classes to convert + final Class[] primitives = { + Boolean.TYPE, Byte.TYPE, Character.TYPE, Short.TYPE, + Integer.TYPE, Long.TYPE, Float.TYPE, Double.TYPE + }; + for (Class primitive : primitives) { + Class wrapperCls = ClassUtils.primitiveToWrapper(primitive); + assertFalse("Still primitive", wrapperCls.isPrimitive()); + assertEquals(wrapperCls + " -> " + primitive, primitive, + ClassUtils.wrapperToPrimitive(wrapperCls)); + } + } + + public void testWrapperToPrimitiveNoWrapper() { + assertNull("Wrong result for non wrapper class", ClassUtils.wrapperToPrimitive(String.class)); + } + + public void testWrapperToPrimitiveNull() { + assertNull("Wrong result for null class", ClassUtils.wrapperToPrimitive(null)); + } + + public void testWrappersToPrimitives() { + // an array with classes to test + final Class[] classes = { + Boolean.class, Byte.class, Character.class, Short.class, + Integer.class, Long.class, Float.class, Double.class, + String.class, ClassUtils.class, null + }; + + Class[] primitives = ClassUtils.wrappersToPrimitives(classes); + // now test the result + assertEquals("Wrong length of result array", classes.length, primitives.length); + for (int i = 0; i < classes.length; i++) { + Class expectedPrimitive = ClassUtils.wrapperToPrimitive(classes[i]); + assertEquals(classes[i] + " -> " + expectedPrimitive, expectedPrimitive, + primitives[i]); + } + } + + public void testWrappersToPrimitivesNull() { +// assertNull("Wrong result for null input", ClassUtils.wrappersToPrimitives(null)); // generates warning + assertNull("Wrong result for null input", ClassUtils.wrappersToPrimitives((Class[]) null)); // equivalent cast + // Other possible casts for null + assertTrue("empty -> empty", Arrays.equals(ArrayUtils.EMPTY_CLASS_ARRAY, ClassUtils.wrappersToPrimitives())); + Class[] castNull = ClassUtils.wrappersToPrimitives((Class)null); // == new Class[]{null} + assertTrue("(Class)null -> [null]", Arrays.equals(new Class[]{null}, castNull)); +} + + public void testWrappersToPrimitivesEmpty() { + Class[] empty = new Class[0]; + assertEquals("Wrong result for empty input", empty, ClassUtils.wrappersToPrimitives(empty)); + } + + public void testGetClassClassNotFound() throws Exception { + assertGetClassThrowsClassNotFound( "bool" ); + assertGetClassThrowsClassNotFound( "bool[]" ); + assertGetClassThrowsClassNotFound( "integer[]" ); + } + + public void testGetClassInvalidArguments() throws Exception { + assertGetClassThrowsNullPointerException( null ); + assertGetClassThrowsClassNotFound( "[][][]" ); + assertGetClassThrowsClassNotFound( "[[]" ); + assertGetClassThrowsClassNotFound( "[" ); + assertGetClassThrowsClassNotFound( "java.lang.String][" ); + assertGetClassThrowsClassNotFound( ".hello.world" ); + assertGetClassThrowsClassNotFound( "hello..world" ); + } + + public void testWithInterleavingWhitespace() throws ClassNotFoundException { + assertEquals( int[].class, ClassUtils.getClass( " int [ ] " ) ); + assertEquals( long[].class, ClassUtils.getClass( "\rlong\t[\n]\r" ) ); + assertEquals( short[].class, ClassUtils.getClass( "\tshort \t\t[]" ) ); + assertEquals( byte[].class, ClassUtils.getClass( "byte[\t\t\n\r] " ) ); + } + + public void testGetInnerClass() throws ClassNotFoundException { + assertEquals( Inner.DeeplyNested.class, ClassUtils.getClass( "org.apache.commons.lang3.ClassUtilsTest.Inner.DeeplyNested" ) ); + assertEquals( Inner.DeeplyNested.class, ClassUtils.getClass( "org.apache.commons.lang3.ClassUtilsTest.Inner$DeeplyNested" ) ); + assertEquals( Inner.DeeplyNested.class, ClassUtils.getClass( "org.apache.commons.lang3.ClassUtilsTest$Inner$DeeplyNested" ) ); + assertEquals( Inner.DeeplyNested.class, ClassUtils.getClass( "org.apache.commons.lang3.ClassUtilsTest$Inner.DeeplyNested" ) ); + } + + public void testGetClassByNormalNameArrays() throws ClassNotFoundException { + assertEquals( int[].class, ClassUtils.getClass( "int[]" ) ); + assertEquals( long[].class, ClassUtils.getClass( "long[]" ) ); + assertEquals( short[].class, ClassUtils.getClass( "short[]" ) ); + assertEquals( byte[].class, ClassUtils.getClass( "byte[]" ) ); + assertEquals( char[].class, ClassUtils.getClass( "char[]" ) ); + assertEquals( float[].class, ClassUtils.getClass( "float[]" ) ); + assertEquals( double[].class, ClassUtils.getClass( "double[]" ) ); + assertEquals( boolean[].class, ClassUtils.getClass( "boolean[]" ) ); + assertEquals( String[].class, ClassUtils.getClass( "java.lang.String[]" ) ); + assertEquals( java.util.Map.Entry[].class, ClassUtils.getClass( "java.util.Map.Entry[]" ) ); + assertEquals( java.util.Map.Entry[].class, ClassUtils.getClass( "java.util.Map$Entry[]" ) ); + assertEquals( java.util.Map.Entry[].class, ClassUtils.getClass( "[Ljava.util.Map.Entry;" ) ); + assertEquals( java.util.Map.Entry[].class, ClassUtils.getClass( "[Ljava.util.Map$Entry;" ) ); + } + + public void testGetClassByNormalNameArrays2D() throws ClassNotFoundException { + assertEquals( int[][].class, ClassUtils.getClass( "int[][]" ) ); + assertEquals( long[][].class, ClassUtils.getClass( "long[][]" ) ); + assertEquals( short[][].class, ClassUtils.getClass( "short[][]" ) ); + assertEquals( byte[][].class, ClassUtils.getClass( "byte[][]" ) ); + assertEquals( char[][].class, ClassUtils.getClass( "char[][]" ) ); + assertEquals( float[][].class, ClassUtils.getClass( "float[][]" ) ); + assertEquals( double[][].class, ClassUtils.getClass( "double[][]" ) ); + assertEquals( boolean[][].class, ClassUtils.getClass( "boolean[][]" ) ); + assertEquals( String[][].class, ClassUtils.getClass( "java.lang.String[][]" ) ); + } + + public void testGetClassWithArrayClasses2D() throws Exception { + assertGetClassReturnsClass( String[][].class ); + assertGetClassReturnsClass( int[][].class ); + assertGetClassReturnsClass( long[][].class ); + assertGetClassReturnsClass( short[][].class ); + assertGetClassReturnsClass( byte[][].class ); + assertGetClassReturnsClass( char[][].class ); + assertGetClassReturnsClass( float[][].class ); + assertGetClassReturnsClass( double[][].class ); + assertGetClassReturnsClass( boolean[][].class ); + } + + public void testGetClassWithArrayClasses() throws Exception { + assertGetClassReturnsClass( String[].class ); + assertGetClassReturnsClass( int[].class ); + assertGetClassReturnsClass( long[].class ); + assertGetClassReturnsClass( short[].class ); + assertGetClassReturnsClass( byte[].class ); + assertGetClassReturnsClass( char[].class ); + assertGetClassReturnsClass( float[].class ); + assertGetClassReturnsClass( double[].class ); + assertGetClassReturnsClass( boolean[].class ); + } + + public void testGetClassRawPrimitives() throws ClassNotFoundException { + assertEquals( int.class, ClassUtils.getClass( "int" ) ); + assertEquals( long.class, ClassUtils.getClass( "long" ) ); + assertEquals( short.class, ClassUtils.getClass( "short" ) ); + assertEquals( byte.class, ClassUtils.getClass( "byte" ) ); + assertEquals( char.class, ClassUtils.getClass( "char" ) ); + assertEquals( float.class, ClassUtils.getClass( "float" ) ); + assertEquals( double.class, ClassUtils.getClass( "double" ) ); + assertEquals( boolean.class, ClassUtils.getClass( "boolean" ) ); + } + + private void assertGetClassReturnsClass( Class c ) throws Exception { + assertEquals( c, ClassUtils.getClass( c.getName() ) ); + } + + private void assertGetClassThrowsException( String className, Class exceptionType ) throws Exception { + try { + ClassUtils.getClass( className ); + fail( "ClassUtils.getClass() should fail with an exception of type " + exceptionType.getName() + " when given class name \"" + className + "\"." ); + } + catch( Exception e ) { + assertTrue( exceptionType.isAssignableFrom( e.getClass() ) ); + } + } + + private void assertGetClassThrowsNullPointerException( String className ) throws Exception { + assertGetClassThrowsException( className, NullPointerException.class ); + } + + private void assertGetClassThrowsClassNotFound( String className ) throws Exception { + assertGetClassThrowsException( className, ClassNotFoundException.class ); + } + + // Show the Java bug: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4071957 + // We may have to delete this if a JDK fixes the bug. + public void testShowJavaBug() throws Exception { + // Tests with Collections$UnmodifiableSet + Set set = Collections.unmodifiableSet(new HashSet()); + Method isEmptyMethod = set.getClass().getMethod("isEmpty", new Class[0]); + try { + isEmptyMethod.invoke(set, new Object[0]); + fail("Failed to throw IllegalAccessException as expected"); + } catch(IllegalAccessException iae) { + // expected + } + } + + public void testGetPublicMethod() throws Exception { + // Tests with Collections$UnmodifiableSet + Set set = Collections.unmodifiableSet(new HashSet()); + Method isEmptyMethod = ClassUtils.getPublicMethod(set.getClass(), "isEmpty", new Class[0]); + assertTrue(Modifier.isPublic(isEmptyMethod.getDeclaringClass().getModifiers())); + + try { + isEmptyMethod.invoke(set, new Object[0]); + } catch(java.lang.IllegalAccessException iae) { + fail("Should not have thrown IllegalAccessException"); + } + + // Tests with a public Class + Method toStringMethod = ClassUtils.getPublicMethod(Object.class, "toString", new Class[0]); + assertEquals(Object.class.getMethod("toString", new Class[0]), toStringMethod); + } + + public void testToClass_object() { +// assertNull(ClassUtils.toClass(null)); // generates warning + assertNull(ClassUtils.toClass((Object[]) null)); // equivalent explicit cast + + // Additional varargs tests + assertTrue("empty -> empty", Arrays.equals(ArrayUtils.EMPTY_CLASS_ARRAY, ClassUtils.toClass())); + Class[] castNull = ClassUtils.toClass((Object) null); // == new Object[]{null} + assertTrue("(Object)null -> [null]", Arrays.equals(new Object[]{null}, castNull)); + + assertSame(ArrayUtils.EMPTY_CLASS_ARRAY, ClassUtils.toClass(ArrayUtils.EMPTY_OBJECT_ARRAY)); + + assertTrue(Arrays.equals(new Class[] { String.class, Integer.class, Double.class }, + ClassUtils.toClass(new Object[] { "Test", Integer.valueOf(1), Double.valueOf(99d) }))); + + assertTrue(Arrays.equals(new Class[] { String.class, null, Double.class }, + ClassUtils.toClass(new Object[] { "Test", null, Double.valueOf(99d) }))); + } + + public void test_getShortCanonicalName_Object() { + assertEquals("", ClassUtils.getShortCanonicalName(null, "")); + assertEquals("ClassUtils", ClassUtils.getShortCanonicalName(new ClassUtils(), "")); + assertEquals("ClassUtils[]", ClassUtils.getShortCanonicalName(new ClassUtils[0], "")); + assertEquals("ClassUtils[][]", ClassUtils.getShortCanonicalName(new ClassUtils[0][0], "")); + assertEquals("int[]", ClassUtils.getShortCanonicalName(new int[0], "")); + assertEquals("int[][]", ClassUtils.getShortCanonicalName(new int[0][0], "")); + + // Inner types + class Named extends Object {} + assertEquals("ClassUtilsTest.6", ClassUtils.getShortCanonicalName(new Object(){}, "")); + assertEquals("ClassUtilsTest.5Named", ClassUtils.getShortCanonicalName(new Named(), "")); + assertEquals("ClassUtilsTest.Inner", ClassUtils.getShortCanonicalName(new Inner(), "")); + } + + public void test_getShortCanonicalName_Class() { + assertEquals("ClassUtils", ClassUtils.getShortCanonicalName(ClassUtils.class)); + assertEquals("ClassUtils[]", ClassUtils.getShortCanonicalName(ClassUtils[].class)); + assertEquals("ClassUtils[][]", ClassUtils.getShortCanonicalName(ClassUtils[][].class)); + assertEquals("int[]", ClassUtils.getShortCanonicalName(int[].class)); + assertEquals("int[][]", ClassUtils.getShortCanonicalName(int[][].class)); + + // Inner types + class Named extends Object {} + assertEquals("ClassUtilsTest.7", ClassUtils.getShortCanonicalName(new Object(){}.getClass())); + assertEquals("ClassUtilsTest.6Named", ClassUtils.getShortCanonicalName(Named.class)); + assertEquals("ClassUtilsTest.Inner", ClassUtils.getShortCanonicalName(Inner.class)); + } + + public void test_getShortCanonicalName_String() { + assertEquals("ClassUtils", ClassUtils.getShortCanonicalName("org.apache.commons.lang3.ClassUtils")); + assertEquals("ClassUtils[]", ClassUtils.getShortCanonicalName("[Lorg.apache.commons.lang3.ClassUtils;")); + assertEquals("ClassUtils[][]", ClassUtils.getShortCanonicalName("[[Lorg.apache.commons.lang3.ClassUtils;")); + assertEquals("ClassUtils[]", ClassUtils.getShortCanonicalName("org.apache.commons.lang3.ClassUtils[]")); + assertEquals("ClassUtils[][]", ClassUtils.getShortCanonicalName("org.apache.commons.lang3.ClassUtils[][]")); + assertEquals("int[]", ClassUtils.getShortCanonicalName("[I")); + assertEquals("int[][]", ClassUtils.getShortCanonicalName("[[I")); + assertEquals("int[]", ClassUtils.getShortCanonicalName("int[]")); + assertEquals("int[][]", ClassUtils.getShortCanonicalName("int[][]")); + + // Inner types + assertEquals("ClassUtilsTest.6", ClassUtils.getShortCanonicalName("org.apache.commons.lang3.ClassUtilsTest$6")); + assertEquals("ClassUtilsTest.5Named", ClassUtils.getShortCanonicalName("org.apache.commons.lang3.ClassUtilsTest$5Named")); + assertEquals("ClassUtilsTest.Inner", ClassUtils.getShortCanonicalName("org.apache.commons.lang3.ClassUtilsTest$Inner")); + } + + public void test_getPackageCanonicalName_Object() { + assertEquals("", ClassUtils.getPackageCanonicalName(null, "")); + assertEquals("org.apache.commons.lang3", ClassUtils.getPackageCanonicalName(new ClassUtils(), "")); + assertEquals("org.apache.commons.lang3", ClassUtils.getPackageCanonicalName(new ClassUtils[0], "")); + assertEquals("org.apache.commons.lang3", ClassUtils.getPackageCanonicalName(new ClassUtils[0][0], "")); + assertEquals("", ClassUtils.getPackageCanonicalName(new int[0], "")); + assertEquals("", ClassUtils.getPackageCanonicalName(new int[0][0], "")); + + // Inner types + class Named extends Object {} + assertEquals("org.apache.commons.lang3", ClassUtils.getPackageCanonicalName(new Object(){}, "")); + assertEquals("org.apache.commons.lang3", ClassUtils.getPackageCanonicalName(new Named(), "")); + assertEquals("org.apache.commons.lang3", ClassUtils.getPackageCanonicalName(new Inner(), "")); + } + + public void test_getPackageCanonicalName_Class() { + assertEquals("org.apache.commons.lang3", ClassUtils.getPackageCanonicalName(ClassUtils.class)); + assertEquals("org.apache.commons.lang3", ClassUtils.getPackageCanonicalName(ClassUtils[].class)); + assertEquals("org.apache.commons.lang3", ClassUtils.getPackageCanonicalName(ClassUtils[][].class)); + assertEquals("", ClassUtils.getPackageCanonicalName(int[].class)); + assertEquals("", ClassUtils.getPackageCanonicalName(int[][].class)); + + // Inner types + class Named extends Object {} + assertEquals("org.apache.commons.lang3", ClassUtils.getPackageCanonicalName(new Object(){}.getClass())); + assertEquals("org.apache.commons.lang3", ClassUtils.getPackageCanonicalName(Named.class)); + assertEquals("org.apache.commons.lang3", ClassUtils.getPackageCanonicalName(Inner.class)); + } + + public void test_getPackageCanonicalName_String() { + assertEquals("org.apache.commons.lang3", + ClassUtils.getPackageCanonicalName("org.apache.commons.lang3.ClassUtils")); + assertEquals("org.apache.commons.lang3", + ClassUtils.getPackageCanonicalName("[Lorg.apache.commons.lang3.ClassUtils;")); + assertEquals("org.apache.commons.lang3", + ClassUtils.getPackageCanonicalName("[[Lorg.apache.commons.lang3.ClassUtils;")); + assertEquals("org.apache.commons.lang3", + ClassUtils.getPackageCanonicalName("org.apache.commons.lang3.ClassUtils[]")); + assertEquals("org.apache.commons.lang3", + ClassUtils.getPackageCanonicalName("org.apache.commons.lang3.ClassUtils[][]")); + assertEquals("", ClassUtils.getPackageCanonicalName("[I")); + assertEquals("", ClassUtils.getPackageCanonicalName("[[I")); + assertEquals("", ClassUtils.getPackageCanonicalName("int[]")); + assertEquals("", ClassUtils.getPackageCanonicalName("int[][]")); + + // Inner types + assertEquals("org.apache.commons.lang3", ClassUtils.getPackageCanonicalName("org.apache.commons.lang3.ClassUtilsTest$6")); + assertEquals("org.apache.commons.lang3", ClassUtils.getPackageCanonicalName("org.apache.commons.lang3.ClassUtilsTest$5Named")); + assertEquals("org.apache.commons.lang3", ClassUtils.getPackageCanonicalName("org.apache.commons.lang3.ClassUtilsTest$Inner")); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/EnumUtils.java b/ApacheCommonsLang/org/apache/commons/lang3/EnumUtils.java new file mode 100644 index 0000000..c72899d --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/EnumUtils.java @@ -0,0 +1,208 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.EnumSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + *

Utility library to provide helper methods for Java enums.

+ * + *

#ThreadSafe#

+ * + * @since 3.0 + * @version $Id: EnumUtils.java 1299413 2012-03-11 17:57:40Z ggregory $ + */ +public class EnumUtils { + + /** + * This constructor is public to permit tools that require a JavaBean + * instance to operate. + */ + public EnumUtils() { + } + + /** + *

Gets the {@code Map} of enums by name.

+ * + *

This method is useful when you need a map of enums by name.

+ * + * @param the type of the enumeration + * @param enumClass the class of the enum to query, not null + * @return the modifiable map of enum names to enums, never null + */ + public static > Map getEnumMap(Class enumClass) { + Map map = new LinkedHashMap(); + for (E e: enumClass.getEnumConstants()) { + map.put(e.name(), e); + } + return map; + } + + /** + *

Gets the {@code List} of enums.

+ * + *

This method is useful when you need a list of enums rather than an array.

+ * + * @param the type of the enumeration + * @param enumClass the class of the enum to query, not null + * @return the modifiable list of enums, never null + */ + public static > List getEnumList(Class enumClass) { + return new ArrayList(Arrays.asList(enumClass.getEnumConstants())); + } + + /** + *

Checks if the specified name is a valid enum for the class.

+ * + *

This method differs from {@link Enum#valueOf} in that checks if the name is + * a valid enum without needing to catch the exception.

+ * + * @param the type of the enumeration + * @param enumClass the class of the enum to query, not null + * @param enumName the enum name, null returns false + * @return true if the enum name is valid, otherwise false + */ + public static > boolean isValidEnum(Class enumClass, String enumName) { + if (enumName == null) { + return false; + } + try { + Enum.valueOf(enumClass, enumName); + return true; + } catch (IllegalArgumentException ex) { + return false; + } + } + + /** + *

Gets the enum for the class, returning {@code null} if not found.

+ * + *

This method differs from {@link Enum#valueOf} in that it does not throw an exception + * for an invalid enum name.

+ * + * @param the type of the enumeration + * @param enumClass the class of the enum to query, not null + * @param enumName the enum name, null returns null + * @return the enum, null if not found + */ + public static > E getEnum(Class enumClass, String enumName) { + if (enumName == null) { + return null; + } + try { + return Enum.valueOf(enumClass, enumName); + } catch (IllegalArgumentException ex) { + return null; + } + } + + /** + *

Creates a long bit vector representation of the given subset of an Enum.

+ * + *

This generates a value that is usable by {@link EnumUtils#processBitVector}.

+ * + *

Do not use this method if you have more than 64 values in your Enum, as this + * would create a value greater than a long can hold.

+ * + * @param enumClass the class of the enum we are working with, not {@code null} + * @param values the values we want to convert, not {@code null} + * @param the type of the enumeration + * @return a long whose binary value represents the given set of enum values. + * @throws NullPointerException if {@code enumClass} or {@code values} is {@code null} + * @throws IllegalArgumentException if {@code enumClass} is not an enum class or has more than 64 values + * @since 3.0.1 + */ + public static > long generateBitVector(Class enumClass, Iterable values) { + checkBitVectorable(enumClass); + Validate.notNull(values); + long total = 0; + for (E constant : values) { + total |= 1 << constant.ordinal(); + } + return total; + } + + /** + *

Creates a long bit vector representation of the given array of Enum values.

+ * + *

This generates a value that is usable by {@link EnumUtils#processBitVector}.

+ * + *

Do not use this method if you have more than 64 values in your Enum, as this + * would create a value greater than a long can hold.

+ * + * @param enumClass the class of the enum we are working with, not {@code null} + * @param values the values we want to convert, not {@code null} + * @param the type of the enumeration + * @return a long whose binary value represents the given set of enum values. + * @throws NullPointerException if {@code enumClass} or {@code values} is {@code null} + * @throws IllegalArgumentException if {@code enumClass} is not an enum class or has more than 64 values + * @since 3.0.1 + */ + public static > long generateBitVector(Class enumClass, E... values) { + Validate.noNullElements(values); + return generateBitVector(enumClass, Arrays. asList(values)); + } + + /** + *

Convert a long value created by {@link EnumUtils#generateBitVector} into the set of + * enum values that it represents.

+ * + *

If you store this value, beware any changes to the enum that would affect ordinal values.

+ * @param enumClass the class of the enum we are working with, not {@code null} + * @param value the long value representation of a set of enum values + * @param the type of the enumeration + * @return a set of enum values + * @throws NullPointerException if {@code enumClass} is {@code null} + * @throws IllegalArgumentException if {@code enumClass} is not an enum class or has more than 64 values + * @since 3.0.1 + */ + public static > EnumSet processBitVector(Class enumClass, long value) { + final E[] constants = checkBitVectorable(enumClass).getEnumConstants(); + final EnumSet results = EnumSet.noneOf(enumClass); + for (E constant : constants) { + if ((value & 1 << constant.ordinal()) != 0) { + results.add(constant); + } + } + return results; + } + + /** + * Validate that {@code enumClass} is compatible with representation in a {@code long}. + * @param the type of the enumeration + * @param enumClass to check + * @return {@code enumClass} + * @throws NullPointerException if {@code enumClass} is {@code null} + * @throws IllegalArgumentException if {@code enumClass} is not an enum class or has more than 64 values + * @since 3.0.1 + */ + private static > Class checkBitVectorable(Class enumClass) { + Validate.notNull(enumClass, "EnumClass must be defined."); + + final E[] constants = enumClass.getEnumConstants(); + Validate.isTrue(constants != null, "%s does not seem to be an Enum type", enumClass); + Validate.isTrue(constants.length <= Long.SIZE, "Cannot store %s %s values in %s bits", constants.length, + enumClass.getSimpleName(), Long.SIZE); + + return enumClass; + } +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/EnumUtilsTest.java b/ApacheCommonsLang/org/apache/commons/lang3/EnumUtilsTest.java new file mode 100644 index 0000000..5dc91a4 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/EnumUtilsTest.java @@ -0,0 +1,201 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.commons.lang3; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.List; +import java.util.Map; + +import org.junit.Test; + +/** + * + * @version $Id: EnumUtilsTest.java 1309910 2012-04-05 15:52:43Z ggregory $ + */ +public class EnumUtilsTest { + + @Test + public void testConstructable() { + // enforce public constructor + new EnumUtils(); + } + + @Test + public void test_getEnumMap() { + Map test = EnumUtils.getEnumMap(Traffic.class); + assertEquals( "getEnumMap not created correctly", "{RED=RED, AMBER=AMBER, GREEN=GREEN}", test.toString()); + assertEquals(3, test.size()); + assertTrue(test.containsKey("RED")); + assertEquals(Traffic.RED, test.get("RED")); + assertTrue(test.containsKey("AMBER")); + assertEquals(Traffic.AMBER, test.get("AMBER")); + assertTrue(test.containsKey("GREEN")); + assertEquals(Traffic.GREEN, test.get("GREEN")); + assertFalse(test.containsKey("PURPLE")); + } + + @Test + public void test_getEnumList() { + List test = EnumUtils.getEnumList(Traffic.class); + assertEquals(3, test.size()); + assertEquals(Traffic.RED, test.get(0)); + assertEquals(Traffic.AMBER, test.get(1)); + assertEquals(Traffic.GREEN, test.get(2)); + } + + @Test + public void test_isEnum() { + assertTrue(EnumUtils.isValidEnum(Traffic.class, "RED")); + assertTrue(EnumUtils.isValidEnum(Traffic.class, "AMBER")); + assertTrue(EnumUtils.isValidEnum(Traffic.class, "GREEN")); + assertFalse(EnumUtils.isValidEnum(Traffic.class, "PURPLE")); + assertFalse(EnumUtils.isValidEnum(Traffic.class, null)); + } + + @Test(expected=NullPointerException.class) + public void test_isEnum_nullClass() { + EnumUtils.isValidEnum((Class) null, "PURPLE"); + } + + @Test + public void test_getEnum() { + assertEquals(Traffic.RED, EnumUtils.getEnum(Traffic.class, "RED")); + assertEquals(Traffic.AMBER, EnumUtils.getEnum(Traffic.class, "AMBER")); + assertEquals(Traffic.GREEN, EnumUtils.getEnum(Traffic.class, "GREEN")); + assertEquals(null, EnumUtils.getEnum(Traffic.class, "PURPLE")); + assertEquals(null, EnumUtils.getEnum(Traffic.class, null)); + } + + @Test(expected=NullPointerException.class) + public void test_getEnum_nullClass() { + EnumUtils.getEnum((Class) null, "PURPLE"); + } + + @Test(expected=NullPointerException.class) + public void test_generateBitVector_nullClass() { + EnumUtils.generateBitVector(null, EnumSet.of(Traffic.RED)); + } + + @Test(expected=NullPointerException.class) + public void test_generateBitVector_nullIterable() { + EnumUtils.generateBitVector(null, (Iterable) null); + } + + @Test(expected=NullPointerException.class) + public void test_generateBitVector_nullClassWithArray() { + EnumUtils.generateBitVector(null, Traffic.RED); + } + + @Test(expected=NullPointerException.class) + public void test_generateBitVector_nullArray() { + EnumUtils.generateBitVector(null, (Traffic[]) null); + } + + @Test(expected=IllegalArgumentException.class) + public void test_generateBitVector_longClass() { + EnumUtils.generateBitVector(TooMany.class, EnumSet.of(TooMany.A1)); + } + + @Test(expected=IllegalArgumentException.class) + public void test_generateBitVector_longClassWithArray() { + EnumUtils.generateBitVector(TooMany.class, TooMany.A1); + } + + @SuppressWarnings("unchecked") + @Test(expected=IllegalArgumentException.class) + public void test_generateBitVector_nonEnumClass() { + @SuppressWarnings("rawtypes") + Class rawType = Object.class; + @SuppressWarnings("rawtypes") + List rawList = new ArrayList(); + EnumUtils.generateBitVector(rawType, rawList); + } + + @SuppressWarnings("unchecked") + @Test(expected=IllegalArgumentException.class) + public void test_generateBitVector_nonEnumClassWithArray() { + @SuppressWarnings("rawtypes") + Class rawType = Object.class; + EnumUtils.generateBitVector(rawType); + } + + @Test + public void test_generateBitVector() { + assertEquals(0L, EnumUtils.generateBitVector(Traffic.class, EnumSet.noneOf(Traffic.class))); + assertEquals(1L, EnumUtils.generateBitVector(Traffic.class, EnumSet.of(Traffic.RED))); + assertEquals(2L, EnumUtils.generateBitVector(Traffic.class, EnumSet.of(Traffic.AMBER))); + assertEquals(4L, EnumUtils.generateBitVector(Traffic.class, EnumSet.of(Traffic.GREEN))); + assertEquals(3L, EnumUtils.generateBitVector(Traffic.class, EnumSet.of(Traffic.RED, Traffic.AMBER))); + assertEquals(5L, EnumUtils.generateBitVector(Traffic.class, EnumSet.of(Traffic.RED, Traffic.GREEN))); + assertEquals(6L, EnumUtils.generateBitVector(Traffic.class, EnumSet.of(Traffic.AMBER, Traffic.GREEN))); + assertEquals(7L, EnumUtils.generateBitVector(Traffic.class, EnumSet.of(Traffic.RED, Traffic.AMBER, Traffic.GREEN))); + } + + @Test + public void test_generateBitVectorFromArray() { + assertEquals(0L, EnumUtils.generateBitVector(Traffic.class)); + assertEquals(1L, EnumUtils.generateBitVector(Traffic.class, Traffic.RED)); + assertEquals(2L, EnumUtils.generateBitVector(Traffic.class, Traffic.AMBER)); + assertEquals(4L, EnumUtils.generateBitVector(Traffic.class, Traffic.GREEN)); + assertEquals(3L, EnumUtils.generateBitVector(Traffic.class, Traffic.RED, Traffic.AMBER)); + assertEquals(5L, EnumUtils.generateBitVector(Traffic.class, Traffic.RED, Traffic.GREEN)); + assertEquals(6L, EnumUtils.generateBitVector(Traffic.class, Traffic.AMBER, Traffic.GREEN)); + assertEquals(7L, EnumUtils.generateBitVector(Traffic.class, Traffic.RED, Traffic.AMBER, Traffic.GREEN)); + //gracefully handles duplicates: + assertEquals(7L, EnumUtils.generateBitVector(Traffic.class, Traffic.RED, Traffic.AMBER, Traffic.GREEN, Traffic.GREEN)); + } + + @Test(expected=NullPointerException.class) + public void test_processBitVector_nullClass() { + final Class empty = null; + EnumUtils.processBitVector(empty, 0L); + } + + @Test(expected=IllegalArgumentException.class) + public void test_processBitVector_longClass() { + EnumUtils.processBitVector(TooMany.class, 0L); + } + + @Test + public void test_processBitVector() { + assertEquals(EnumSet.noneOf(Traffic.class), EnumUtils.processBitVector(Traffic.class, 0L)); + assertEquals(EnumSet.of(Traffic.RED), EnumUtils.processBitVector(Traffic.class, 1L)); + assertEquals(EnumSet.of(Traffic.AMBER), EnumUtils.processBitVector(Traffic.class, 2L)); + assertEquals(EnumSet.of(Traffic.RED, Traffic.AMBER), EnumUtils.processBitVector(Traffic.class, 3L)); + assertEquals(EnumSet.of(Traffic.GREEN), EnumUtils.processBitVector(Traffic.class, 4L)); + assertEquals(EnumSet.of(Traffic.RED, Traffic.GREEN), EnumUtils.processBitVector(Traffic.class, 5L)); + assertEquals(EnumSet.of(Traffic.AMBER, Traffic.GREEN), EnumUtils.processBitVector(Traffic.class, 6L)); + assertEquals(EnumSet.of(Traffic.RED, Traffic.AMBER, Traffic.GREEN), EnumUtils.processBitVector(Traffic.class, 7L)); + } +} + +enum Traffic { + RED, AMBER, GREEN +} + +enum TooMany { + A,B,C,D,E,F,G,H,I,J,K,L,M,N,O,P,Q,R,S,T,U,V,W,X,Y,Z, + A1,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,M1,N1,O1,P1,Q1,R1,S1,T1,U1,V1,W1,X1,Y1,Z1, + A2,B2,C2,D2,E2,F2,G2,H2,I2,J2,K2,L2,M2; +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/JavaVersion.java b/ApacheCommonsLang/org/apache/commons/lang3/JavaVersion.java new file mode 100644 index 0000000..4901f6d --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/JavaVersion.java @@ -0,0 +1,168 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3; + +/** + *

An enum representing all the versions of the Java specification. + * This is intended to mirror available values from the + * java.specification.version System property.

+ * + * @since 3.0 + * @version $Id: $ + */ +public enum JavaVersion { + + /** + * The Java version reported by Android. This is not an official Java version number. + */ + JAVA_0_9(1.5f, "0.9"), + + /** + * Java 1.1. + */ + JAVA_1_1(1.1f, "1.1"), + + /** + * Java 1.2. + */ + JAVA_1_2(1.2f, "1.2"), + + /** + * Java 1.3. + */ + JAVA_1_3(1.3f, "1.3"), + + /** + * Java 1.4. + */ + JAVA_1_4(1.4f, "1.4"), + + /** + * Java 1.5. + */ + JAVA_1_5(1.5f, "1.5"), + + /** + * Java 1.6. + */ + JAVA_1_6(1.6f, "1.6"), + + /** + * Java 1.7. + */ + JAVA_1_7(1.7f, "1.7"), + + /** + * Java 1.8. + */ + JAVA_1_8(1.8f, "1.8"); + + /** + * The float value. + */ + private float value; + /** + * The standard name. + */ + private String name; + + /** + * Constructor. + * + * @param value the float value + * @param name the standard name, not null + */ + JavaVersion(final float value, final String name) { + this.value = value; + this.name = name; + } + + //----------------------------------------------------------------------- + /** + *

Whether this version of Java is at least the version of Java passed in.

+ * + *

For example:
+ * {@code myVersion.atLeast(JavaVersion.JAVA_1_4)}

+ * + * @param requiredVersion the version to check against, not null + * @return true if this version is equal to or greater than the specified version + */ + public boolean atLeast(JavaVersion requiredVersion) { + return this.value >= requiredVersion.value; + } + + /** + * Transforms the given string with a Java version number to the + * corresponding constant of this enumeration class. This method is used + * internally. + * + * @param nom the Java version as string + * @return the corresponding enumeration constant or null if the + * version is unknown + */ + // helper for static importing + static JavaVersion getJavaVersion(final String nom) { + return get(nom); + } + + /** + * Transforms the given string with a Java version number to the + * corresponding constant of this enumeration class. This method is used + * internally. + * + * @param nom the Java version as string + * @return the corresponding enumeration constant or null if the + * version is unknown + */ + static JavaVersion get(final String nom) { + if ("0.9".equals(nom)) { + return JAVA_0_9; + } else if ("1.1".equals(nom)) { + return JAVA_1_1; + } else if ("1.2".equals(nom)) { + return JAVA_1_2; + } else if ("1.3".equals(nom)) { + return JAVA_1_3; + } else if ("1.4".equals(nom)) { + return JAVA_1_4; + } else if ("1.5".equals(nom)) { + return JAVA_1_5; + } else if ("1.6".equals(nom)) { + return JAVA_1_6; + } else if ("1.7".equals(nom)) { + return JAVA_1_7; + } else if ("1.8".equals(nom)) { + return JAVA_1_8; + } else { + return null; + } + } + + //----------------------------------------------------------------------- + /** + *

The string value is overridden to return the standard name.

+ * + *

For example, "1.5".

+ * + * @return the name, not null + */ + @Override + public String toString() { + return name; + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/JavaVersionTest.java b/ApacheCommonsLang/org/apache/commons/lang3/JavaVersionTest.java new file mode 100644 index 0000000..f282fc5 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/JavaVersionTest.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.commons.lang3; + +import static org.apache.commons.lang3.JavaVersion.JAVA_0_9; +import static org.apache.commons.lang3.JavaVersion.JAVA_1_1; +import static org.apache.commons.lang3.JavaVersion.JAVA_1_2; +import static org.apache.commons.lang3.JavaVersion.JAVA_1_3; +import static org.apache.commons.lang3.JavaVersion.JAVA_1_4; +import static org.apache.commons.lang3.JavaVersion.JAVA_1_5; +import static org.apache.commons.lang3.JavaVersion.JAVA_1_6; +import static org.apache.commons.lang3.JavaVersion.JAVA_1_7; +import static org.apache.commons.lang3.JavaVersion.JAVA_1_8; +import static org.apache.commons.lang3.JavaVersion.get; +import static org.apache.commons.lang3.JavaVersion.getJavaVersion; +import junit.framework.TestCase; + +/** + * Unit tests {@link org.apache.commons.lang3.JavaVersion}. + * + * @version $Id: JavaVersionTest.java 918366 2010-03-03 08:56:22Z bayard $ + */ +public class JavaVersionTest extends TestCase { + + public void testGetJavaVersion() { + assertEquals("0.9 failed", JAVA_0_9, get("0.9")); + assertEquals("1.1 failed", JAVA_1_1, get("1.1")); + assertEquals("1.2 failed", JAVA_1_2, get("1.2")); + assertEquals("1.3 failed", JAVA_1_3, get("1.3")); + assertEquals("1.4 failed", JAVA_1_4, get("1.4")); + assertEquals("1.5 failed", JAVA_1_5, get("1.5")); + assertEquals("1.6 failed", JAVA_1_6, get("1.6")); + assertEquals("1.7 failed", JAVA_1_7, get("1.7")); + assertEquals("1.8 failed", JAVA_1_8, get("1.8")); + assertNull("1.9 unexpectedly worked", get("1.9")); + assertEquals("Wrapper method failed", get("1.5"), getJavaVersion("1.5")); + } + + public void testAtLeast() { + assertFalse("1.2 at least 1.5 passed", JAVA_1_2.atLeast(JAVA_1_5)); + assertTrue("1.5 at least 1.2 failed", JAVA_1_5.atLeast(JAVA_1_2)); + assertFalse("1.6 at least 1.7 passed", JAVA_1_6.atLeast(JAVA_1_7)); + + assertTrue("0.9 at least 1.5 failed", JAVA_0_9.atLeast(JAVA_1_5)); + assertFalse("0.9 at least 1.6 passed", JAVA_0_9.atLeast(JAVA_1_6)); + } + + public void testToString() { + assertEquals("1.2", JAVA_1_2.toString()); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/LocaleUtils.java b/ApacheCommonsLang/org/apache/commons/lang3/LocaleUtils.java new file mode 100644 index 0000000..a280f46 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/LocaleUtils.java @@ -0,0 +1,297 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +/** + *

Operations to assist when working with a {@link Locale}.

+ * + *

This class tries to handle {@code null} input gracefully. + * An exception will not be thrown for a {@code null} input. + * Each method documents its behaviour in more detail.

+ * + * @since 2.2 + * @version $Id: LocaleUtils.java 1342651 2012-05-25 14:39:38Z sebb $ + */ +public class LocaleUtils { + + /** Concurrent map of language locales by country. */ + private static final ConcurrentMap> cLanguagesByCountry = + new ConcurrentHashMap>(); + + /** Concurrent map of country locales by language. */ + private static final ConcurrentMap> cCountriesByLanguage = + new ConcurrentHashMap>(); + + /** + *

{@code LocaleUtils} instances should NOT be constructed in standard programming. + * Instead, the class should be used as {@code LocaleUtils.toLocale("en_GB");}.

+ * + *

This constructor is public to permit tools that require a JavaBean instance + * to operate.

+ */ + public LocaleUtils() { + super(); + } + + //----------------------------------------------------------------------- + /** + *

Converts a String to a Locale.

+ * + *

This method takes the string format of a locale and creates the + * locale object from it.

+ * + *
+     *   LocaleUtils.toLocale("en")         = new Locale("en", "")
+     *   LocaleUtils.toLocale("en_GB")      = new Locale("en", "GB")
+     *   LocaleUtils.toLocale("en_GB_xxx")  = new Locale("en", "GB", "xxx")   (#)
+     * 
+ * + *

(#) The behaviour of the JDK variant constructor changed between JDK1.3 and JDK1.4. + * In JDK1.3, the constructor upper cases the variant, in JDK1.4, it doesn't. + * Thus, the result from getVariant() may vary depending on your JDK.

+ * + *

This method validates the input strictly. + * The language code must be lowercase. + * The country code must be uppercase. + * The separator must be an underscore. + * The length must be correct. + *

+ * + * @param str the locale String to convert, null returns null + * @return a Locale, null if null input + * @throws IllegalArgumentException if the string is an invalid format + */ + public static Locale toLocale(String str) { + if (str == null) { + return null; + } + int len = str.length(); + if (len != 2 && len != 5 && len < 7) { + throw new IllegalArgumentException("Invalid locale format: " + str); + } + char ch0 = str.charAt(0); + char ch1 = str.charAt(1); + if (ch0 < 'a' || ch0 > 'z' || ch1 < 'a' || ch1 > 'z') { + throw new IllegalArgumentException("Invalid locale format: " + str); + } + if (len == 2) { + return new Locale(str, ""); + } else { + if (str.charAt(2) != '_') { + throw new IllegalArgumentException("Invalid locale format: " + str); + } + char ch3 = str.charAt(3); + if (ch3 == '_') { + return new Locale(str.substring(0, 2), "", str.substring(4)); + } + char ch4 = str.charAt(4); + if (ch3 < 'A' || ch3 > 'Z' || ch4 < 'A' || ch4 > 'Z') { + throw new IllegalArgumentException("Invalid locale format: " + str); + } + if (len == 5) { + return new Locale(str.substring(0, 2), str.substring(3, 5)); + } else { + if (str.charAt(5) != '_') { + throw new IllegalArgumentException("Invalid locale format: " + str); + } + return new Locale(str.substring(0, 2), str.substring(3, 5), str.substring(6)); + } + } + } + + //----------------------------------------------------------------------- + /** + *

Obtains the list of locales to search through when performing + * a locale search.

+ * + *
+     * localeLookupList(Locale("fr","CA","xxx"))
+     *   = [Locale("fr","CA","xxx"), Locale("fr","CA"), Locale("fr")]
+     * 
+ * + * @param locale the locale to start from + * @return the unmodifiable list of Locale objects, 0 being locale, not null + */ + public static List localeLookupList(Locale locale) { + return localeLookupList(locale, locale); + } + + //----------------------------------------------------------------------- + /** + *

Obtains the list of locales to search through when performing + * a locale search.

+ * + *
+     * localeLookupList(Locale("fr", "CA", "xxx"), Locale("en"))
+     *   = [Locale("fr","CA","xxx"), Locale("fr","CA"), Locale("fr"), Locale("en"]
+     * 
+ * + *

The result list begins with the most specific locale, then the + * next more general and so on, finishing with the default locale. + * The list will never contain the same locale twice.

+ * + * @param locale the locale to start from, null returns empty list + * @param defaultLocale the default locale to use if no other is found + * @return the unmodifiable list of Locale objects, 0 being locale, not null + */ + public static List localeLookupList(Locale locale, Locale defaultLocale) { + List list = new ArrayList(4); + if (locale != null) { + list.add(locale); + if (locale.getVariant().length() > 0) { + list.add(new Locale(locale.getLanguage(), locale.getCountry())); + } + if (locale.getCountry().length() > 0) { + list.add(new Locale(locale.getLanguage(), "")); + } + if (list.contains(defaultLocale) == false) { + list.add(defaultLocale); + } + } + return Collections.unmodifiableList(list); + } + + //----------------------------------------------------------------------- + /** + *

Obtains an unmodifiable list of installed locales.

+ * + *

This method is a wrapper around {@link Locale#getAvailableLocales()}. + * It is more efficient, as the JDK method must create a new array each + * time it is called.

+ * + * @return the unmodifiable list of available locales + */ + public static List availableLocaleList() { + return SyncAvoid.AVAILABLE_LOCALE_LIST; + } + + //----------------------------------------------------------------------- + /** + *

Obtains an unmodifiable set of installed locales.

+ * + *

This method is a wrapper around {@link Locale#getAvailableLocales()}. + * It is more efficient, as the JDK method must create a new array each + * time it is called.

+ * + * @return the unmodifiable set of available locales + */ + public static Set availableLocaleSet() { + return SyncAvoid.AVAILABLE_LOCALE_SET; + } + + //----------------------------------------------------------------------- + /** + *

Checks if the locale specified is in the list of available locales.

+ * + * @param locale the Locale object to check if it is available + * @return true if the locale is a known locale + */ + public static boolean isAvailableLocale(Locale locale) { + return availableLocaleList().contains(locale); + } + + //----------------------------------------------------------------------- + /** + *

Obtains the list of languages supported for a given country.

+ * + *

This method takes a country code and searches to find the + * languages available for that country. Variant locales are removed.

+ * + * @param countryCode the 2 letter country code, null returns empty + * @return an unmodifiable List of Locale objects, not null + */ + public static List languagesByCountry(String countryCode) { + if (countryCode == null) { + return Collections.emptyList(); + } + List langs = cLanguagesByCountry.get(countryCode); + if (langs == null) { + langs = new ArrayList(); + List locales = availableLocaleList(); + for (int i = 0; i < locales.size(); i++) { + Locale locale = locales.get(i); + if (countryCode.equals(locale.getCountry()) && + locale.getVariant().isEmpty()) { + langs.add(locale); + } + } + langs = Collections.unmodifiableList(langs); + cLanguagesByCountry.putIfAbsent(countryCode, langs); + langs = cLanguagesByCountry.get(countryCode); + } + return langs; + } + + //----------------------------------------------------------------------- + /** + *

Obtains the list of countries supported for a given language.

+ * + *

This method takes a language code and searches to find the + * countries available for that language. Variant locales are removed.

+ * + * @param languageCode the 2 letter language code, null returns empty + * @return an unmodifiable List of Locale objects, not null + */ + public static List countriesByLanguage(String languageCode) { + if (languageCode == null) { + return Collections.emptyList(); + } + List countries = cCountriesByLanguage.get(languageCode); + if (countries == null) { + countries = new ArrayList(); + List locales = availableLocaleList(); + for (int i = 0; i < locales.size(); i++) { + Locale locale = locales.get(i); + if (languageCode.equals(locale.getLanguage()) && + locale.getCountry().length() != 0 && + locale.getVariant().isEmpty()) { + countries.add(locale); + } + } + countries = Collections.unmodifiableList(countries); + cCountriesByLanguage.putIfAbsent(languageCode, countries); + countries = cCountriesByLanguage.get(languageCode); + } + return countries; + } + + //----------------------------------------------------------------------- + // class to avoid synchronization (Init on demand) + static class SyncAvoid { + /** Unmodifiable list of available locales. */ + private static final List AVAILABLE_LOCALE_LIST; + /** Unmodifiable set of available locales. */ + private static final Set AVAILABLE_LOCALE_SET; + + static { + List list = new ArrayList(Arrays.asList(Locale.getAvailableLocales())); // extra safe + AVAILABLE_LOCALE_LIST = Collections.unmodifiableList(list); + AVAILABLE_LOCALE_SET = Collections.unmodifiableSet(new HashSet(list)); + } + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/LocaleUtilsTest.java b/ApacheCommonsLang/org/apache/commons/lang3/LocaleUtilsTest.java new file mode 100644 index 0000000..51afb59 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/LocaleUtilsTest.java @@ -0,0 +1,488 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3; + +import static org.apache.commons.lang3.JavaVersion.JAVA_1_4; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Modifier; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Set; + +import junit.framework.TestCase; + +/** + * Unit tests for {@link LocaleUtils}. + * + * @version $Id: LocaleUtilsTest.java 1299411 2012-03-11 17:55:29Z ggregory $ + */ +public class LocaleUtilsTest extends TestCase { + + private static final Locale LOCALE_EN = new Locale("en", ""); + private static final Locale LOCALE_EN_US = new Locale("en", "US"); + private static final Locale LOCALE_EN_US_ZZZZ = new Locale("en", "US", "ZZZZ"); + private static final Locale LOCALE_FR = new Locale("fr", ""); + private static final Locale LOCALE_FR_CA = new Locale("fr", "CA"); + private static final Locale LOCALE_QQ = new Locale("qq", ""); + private static final Locale LOCALE_QQ_ZZ = new Locale("qq", "ZZ"); + + /** + * Constructor. + * + * @param name + */ + public LocaleUtilsTest(String name) { + super(name); + } + + @Override + public void setUp() throws Exception { + super.setUp(); + + // Testing #LANG-304. Must be called before availableLocaleSet is called. + LocaleUtils.isAvailableLocale(Locale.getDefault()); + } + + //----------------------------------------------------------------------- + /** + * Test that constructors are public, and work, etc. + */ + public void testConstructor() { + assertNotNull(new LocaleUtils()); + Constructor[] cons = LocaleUtils.class.getDeclaredConstructors(); + assertEquals(1, cons.length); + assertEquals(true, Modifier.isPublic(cons[0].getModifiers())); + assertEquals(true, Modifier.isPublic(LocaleUtils.class.getModifiers())); + assertEquals(false, Modifier.isFinal(LocaleUtils.class.getModifiers())); + } + + //----------------------------------------------------------------------- + /** + * Pass in a valid language, test toLocale. + * + * @param language the language string + */ + private void assertValidToLocale(String language) { + Locale locale = LocaleUtils.toLocale(language); + assertNotNull("valid locale", locale); + assertEquals(language, locale.getLanguage()); + //country and variant are empty + assertTrue(locale.getCountry() == null || locale.getCountry().isEmpty()); + assertTrue(locale.getVariant() == null || locale.getVariant().isEmpty()); + } + + /** + * Pass in a valid language, test toLocale. + * + * @param localeString to pass to toLocale() + * @param language of the resulting Locale + * @param country of the resulting Locale + */ + private void assertValidToLocale(String localeString, String language, String country) { + Locale locale = LocaleUtils.toLocale(localeString); + assertNotNull("valid locale", locale); + assertEquals(language, locale.getLanguage()); + assertEquals(country, locale.getCountry()); + //variant is empty + assertTrue(locale.getVariant() == null || locale.getVariant().isEmpty()); + } + + /** + * Pass in a valid language, test toLocale. + * + * @param localeString to pass to toLocale() + * @param language of the resulting Locale + * @param country of the resulting Locale + * @param variant of the resulting Locale + */ + private void assertValidToLocale( + String localeString, String language, + String country, String variant) { + Locale locale = LocaleUtils.toLocale(localeString); + assertNotNull("valid locale", locale); + assertEquals(language, locale.getLanguage()); + assertEquals(country, locale.getCountry()); + assertEquals(variant, locale.getVariant()); + + } + + /** + * Test toLocale() method. + */ + public void testToLocale_1Part() { + assertEquals(null, LocaleUtils.toLocale((String) null)); + + assertValidToLocale("us"); + assertValidToLocale("fr"); + assertValidToLocale("de"); + assertValidToLocale("zh"); + // Valid format but lang doesnt exist, should make instance anyway + assertValidToLocale("qq"); + + try { + LocaleUtils.toLocale("Us"); + fail("Should fail if not lowercase"); + } catch (IllegalArgumentException iae) {} + try { + LocaleUtils.toLocale("US"); + fail("Should fail if not lowercase"); + } catch (IllegalArgumentException iae) {} + try { + LocaleUtils.toLocale("uS"); + fail("Should fail if not lowercase"); + } catch (IllegalArgumentException iae) {} + try { + LocaleUtils.toLocale("u#"); + fail("Should fail if not lowercase"); + } catch (IllegalArgumentException iae) {} + + try { + LocaleUtils.toLocale("u"); + fail("Must be 2 chars if less than 5"); + } catch (IllegalArgumentException iae) {} + + try { + LocaleUtils.toLocale("uuu"); + fail("Must be 2 chars if less than 5"); + } catch (IllegalArgumentException iae) {} + + try { + LocaleUtils.toLocale("uu_U"); + fail("Must be 2 chars if less than 5"); + } catch (IllegalArgumentException iae) {} + } + + /** + * Test toLocale() method. + */ + public void testToLocale_2Part() { + assertValidToLocale("us_EN", "us", "EN"); + //valid though doesnt exist + assertValidToLocale("us_ZH", "us", "ZH"); + + try { + LocaleUtils.toLocale("us-EN"); + fail("Should fail as not underscore"); + } catch (IllegalArgumentException iae) {} + try { + LocaleUtils.toLocale("us_En"); + fail("Should fail second part not uppercase"); + } catch (IllegalArgumentException iae) {} + try { + LocaleUtils.toLocale("us_en"); + fail("Should fail second part not uppercase"); + } catch (IllegalArgumentException iae) {} + try { + LocaleUtils.toLocale("us_eN"); + fail("Should fail second part not uppercase"); + } catch (IllegalArgumentException iae) {} + try { + LocaleUtils.toLocale("uS_EN"); + fail("Should fail first part not lowercase"); + } catch (IllegalArgumentException iae) {} + try { + LocaleUtils.toLocale("us_E3"); + fail("Should fail second part not uppercase"); + } catch (IllegalArgumentException iae) {} + } + + /** + * Test toLocale() method. + */ + public void testToLocale_3Part() { + assertValidToLocale("us_EN_A", "us", "EN", "A"); + // this isn't pretty, but was caused by a jdk bug it seems + // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4210525 + if (SystemUtils.isJavaVersionAtLeast(JAVA_1_4)) { + assertValidToLocale("us_EN_a", "us", "EN", "a"); + assertValidToLocale("us_EN_SFsafdFDsdfF", "us", "EN", "SFsafdFDsdfF"); + } else { + assertValidToLocale("us_EN_a", "us", "EN", "A"); + assertValidToLocale("us_EN_SFsafdFDsdfF", "us", "EN", "SFSAFDFDSDFF"); + } + + try { + LocaleUtils.toLocale("us_EN-a"); + fail("Should fail as not underscore"); + } catch (IllegalArgumentException iae) {} + try { + LocaleUtils.toLocale("uu_UU_"); + fail("Must be 3, 5 or 7+ in length"); + } catch (IllegalArgumentException iae) {} + } + + //----------------------------------------------------------------------- + /** + * Helper method for local lookups. + * + * @param locale the input locale + * @param defaultLocale the input default locale + * @param expected expected results + */ + private void assertLocaleLookupList(Locale locale, Locale defaultLocale, Locale[] expected) { + List localeList = defaultLocale == null ? + LocaleUtils.localeLookupList(locale) : + LocaleUtils.localeLookupList(locale, defaultLocale); + + assertEquals(expected.length, localeList.size()); + assertEquals(Arrays.asList(expected), localeList); + assertUnmodifiableCollection(localeList); + } + + //----------------------------------------------------------------------- + /** + * Test localeLookupList() method. + */ + public void testLocaleLookupList_Locale() { + assertLocaleLookupList(null, null, new Locale[0]); + assertLocaleLookupList(LOCALE_QQ, null, new Locale[]{LOCALE_QQ}); + assertLocaleLookupList(LOCALE_EN, null, new Locale[]{LOCALE_EN}); + assertLocaleLookupList(LOCALE_EN, null, new Locale[]{LOCALE_EN}); + assertLocaleLookupList(LOCALE_EN_US, null, + new Locale[] { + LOCALE_EN_US, + LOCALE_EN}); + assertLocaleLookupList(LOCALE_EN_US_ZZZZ, null, + new Locale[] { + LOCALE_EN_US_ZZZZ, + LOCALE_EN_US, + LOCALE_EN}); + } + + /** + * Test localeLookupList() method. + */ + public void testLocaleLookupList_LocaleLocale() { + assertLocaleLookupList(LOCALE_QQ, LOCALE_QQ, + new Locale[]{LOCALE_QQ}); + assertLocaleLookupList(LOCALE_EN, LOCALE_EN, + new Locale[]{LOCALE_EN}); + + assertLocaleLookupList(LOCALE_EN_US, LOCALE_EN_US, + new Locale[]{ + LOCALE_EN_US, + LOCALE_EN}); + assertLocaleLookupList(LOCALE_EN_US, LOCALE_QQ, + new Locale[] { + LOCALE_EN_US, + LOCALE_EN, + LOCALE_QQ}); + assertLocaleLookupList(LOCALE_EN_US, LOCALE_QQ_ZZ, + new Locale[] { + LOCALE_EN_US, + LOCALE_EN, + LOCALE_QQ_ZZ}); + + assertLocaleLookupList(LOCALE_EN_US_ZZZZ, null, + new Locale[] { + LOCALE_EN_US_ZZZZ, + LOCALE_EN_US, + LOCALE_EN}); + assertLocaleLookupList(LOCALE_EN_US_ZZZZ, LOCALE_EN_US_ZZZZ, + new Locale[] { + LOCALE_EN_US_ZZZZ, + LOCALE_EN_US, + LOCALE_EN}); + assertLocaleLookupList(LOCALE_EN_US_ZZZZ, LOCALE_QQ, + new Locale[] { + LOCALE_EN_US_ZZZZ, + LOCALE_EN_US, + LOCALE_EN, + LOCALE_QQ}); + assertLocaleLookupList(LOCALE_EN_US_ZZZZ, LOCALE_QQ_ZZ, + new Locale[] { + LOCALE_EN_US_ZZZZ, + LOCALE_EN_US, + LOCALE_EN, + LOCALE_QQ_ZZ}); + assertLocaleLookupList(LOCALE_FR_CA, LOCALE_EN, + new Locale[] { + LOCALE_FR_CA, + LOCALE_FR, + LOCALE_EN}); + } + + //----------------------------------------------------------------------- + /** + * Test availableLocaleList() method. + */ + public void testAvailableLocaleList() { + List list = LocaleUtils.availableLocaleList(); + List list2 = LocaleUtils.availableLocaleList(); + assertNotNull(list); + assertSame(list, list2); + assertUnmodifiableCollection(list); + + Locale[] jdkLocaleArray = Locale.getAvailableLocales(); + List jdkLocaleList = Arrays.asList(jdkLocaleArray); + assertEquals(jdkLocaleList, list); + } + + //----------------------------------------------------------------------- + /** + * Test availableLocaleSet() method. + */ + public void testAvailableLocaleSet() { + Set set = LocaleUtils.availableLocaleSet(); + Set set2 = LocaleUtils.availableLocaleSet(); + assertNotNull(set); + assertSame(set, set2); + assertUnmodifiableCollection(set); + + Locale[] jdkLocaleArray = Locale.getAvailableLocales(); + List jdkLocaleList = Arrays.asList(jdkLocaleArray); + Set jdkLocaleSet = new HashSet(jdkLocaleList); + assertEquals(jdkLocaleSet, set); + } + + //----------------------------------------------------------------------- + /** + * Test availableLocaleSet() method. + */ + public void testIsAvailableLocale() { + Set set = LocaleUtils.availableLocaleSet(); + assertEquals(set.contains(LOCALE_EN), LocaleUtils.isAvailableLocale(LOCALE_EN)); + assertEquals(set.contains(LOCALE_EN_US), LocaleUtils.isAvailableLocale(LOCALE_EN_US)); + assertEquals(set.contains(LOCALE_EN_US_ZZZZ), LocaleUtils.isAvailableLocale(LOCALE_EN_US_ZZZZ)); + assertEquals(set.contains(LOCALE_FR), LocaleUtils.isAvailableLocale(LOCALE_FR)); + assertEquals(set.contains(LOCALE_FR_CA), LocaleUtils.isAvailableLocale(LOCALE_FR_CA)); + assertEquals(set.contains(LOCALE_QQ), LocaleUtils.isAvailableLocale(LOCALE_QQ)); + assertEquals(set.contains(LOCALE_QQ_ZZ), LocaleUtils.isAvailableLocale(LOCALE_QQ_ZZ)); + } + + //----------------------------------------------------------------------- + /** + * Make sure the language by country is correct. It checks that + * the LocaleUtils.languagesByCountry(country) call contains the + * array of languages passed in. It may contain more due to JVM + * variations. + * + * @param country + * @param languages array of languages that should be returned + */ + private void assertLanguageByCountry(String country, String[] languages) { + List list = LocaleUtils.languagesByCountry(country); + List list2 = LocaleUtils.languagesByCountry(country); + assertNotNull(list); + assertSame(list, list2); + //search through langauges + for (String language : languages) { + Iterator iterator = list.iterator(); + boolean found = false; + // see if it was returned by the set + while (iterator.hasNext()) { + Locale locale = iterator.next(); + // should have an en empty variant + assertTrue(locale.getVariant() == null + || locale.getVariant().isEmpty()); + assertEquals(country, locale.getCountry()); + if (language.equals(locale.getLanguage())) { + found = true; + break; + } + } + if (!found) { + fail("Cound not find language: " + language + + " for country: " + country); + } + } + assertUnmodifiableCollection(list); + } + + /** + * Test languagesByCountry() method. + */ + public void testLanguagesByCountry() { + assertLanguageByCountry(null, new String[0]); + assertLanguageByCountry("GB", new String[]{"en"}); + assertLanguageByCountry("ZZ", new String[0]); + assertLanguageByCountry("CH", new String[]{"fr", "de", "it"}); + } + + //----------------------------------------------------------------------- + /** + * Make sure the country by language is correct. It checks that + * the LocaleUtils.countryByLanguage(language) call contains the + * array of countries passed in. It may contain more due to JVM + * variations. + * + * + * @param language + * @param countries array of countries that should be returned + */ + private void assertCountriesByLanguage(String language, String[] countries) { + List list = LocaleUtils.countriesByLanguage(language); + List list2 = LocaleUtils.countriesByLanguage(language); + assertNotNull(list); + assertSame(list, list2); + //search through langauges + for (String countrie : countries) { + Iterator iterator = list.iterator(); + boolean found = false; + // see if it was returned by the set + while (iterator.hasNext()) { + Locale locale = iterator.next(); + // should have an en empty variant + assertTrue(locale.getVariant() == null + || locale.getVariant().isEmpty()); + assertEquals(language, locale.getLanguage()); + if (countrie.equals(locale.getCountry())) { + found = true; + break; + } + } + if (!found) { + fail("Cound not find language: " + countrie + + " for country: " + language); + } + } + assertUnmodifiableCollection(list); + } + + /** + * Test countriesByLanguage() method. + */ + public void testCountriesByLanguage() { + assertCountriesByLanguage(null, new String[0]); + assertCountriesByLanguage("de", new String[]{"DE", "CH", "AT", "LU"}); + assertCountriesByLanguage("zz", new String[0]); + assertCountriesByLanguage("it", new String[]{"IT", "CH"}); + } + + /** + * @param coll the collection to check + */ + private static void assertUnmodifiableCollection(Collection coll) { + try { + coll.add(null); + fail(); + } catch (UnsupportedOperationException ex) {} + } + + /** + * Tests #LANG-328 - only language+variant + */ + public void testLang328() { + assertValidToLocale("fr__POSIX", "fr", "", "POSIX"); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/ObjectUtils.java b/ApacheCommonsLang/org/apache/commons/lang3/ObjectUtils.java new file mode 100644 index 0000000..2343d95 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/ObjectUtils.java @@ -0,0 +1,608 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3; + +import java.io.Serializable; +import java.lang.reflect.Array; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Map; +import java.util.TreeSet; + +import org.apache.commons.lang3.exception.CloneFailedException; +import org.apache.commons.lang3.mutable.MutableInt; + +/** + *

Operations on {@code Object}.

+ * + *

This class tries to handle {@code null} input gracefully. + * An exception will generally not be thrown for a {@code null} input. + * Each method documents its behaviour in more detail.

+ * + *

#ThreadSafe#

+ * @since 1.0 + * @version $Id: ObjectUtils.java 1199894 2011-11-09 17:53:59Z ggregory $ + */ +//@Immutable +public class ObjectUtils { + + /** + *

Singleton used as a {@code null} placeholder where + * {@code null} has another meaning.

+ * + *

For example, in a {@code HashMap} the + * {@link java.util.HashMap#get(java.lang.Object)} method returns + * {@code null} if the {@code Map} contains {@code null} or if there + * is no matching key. The {@code Null} placeholder can be used to + * distinguish between these two cases.

+ * + *

Another example is {@code Hashtable}, where {@code null} + * cannot be stored.

+ * + *

This instance is Serializable.

+ */ + public static final Null NULL = new Null(); + + /** + *

{@code ObjectUtils} instances should NOT be constructed in + * standard programming. Instead, the static methods on the class should + * be used, such as {@code ObjectUtils.defaultIfNull("a","b");}.

+ * + *

This constructor is public to permit tools that require a JavaBean + * instance to operate.

+ */ + public ObjectUtils() { + super(); + } + + // Defaulting + //----------------------------------------------------------------------- + /** + *

Returns a default value if the object passed is {@code null}.

+ * + *
+     * ObjectUtils.defaultIfNull(null, null)      = null
+     * ObjectUtils.defaultIfNull(null, "")        = ""
+     * ObjectUtils.defaultIfNull(null, "zz")      = "zz"
+     * ObjectUtils.defaultIfNull("abc", *)        = "abc"
+     * ObjectUtils.defaultIfNull(Boolean.TRUE, *) = Boolean.TRUE
+     * 
+ * + * @param the type of the object + * @param object the {@code Object} to test, may be {@code null} + * @param defaultValue the default value to return, may be {@code null} + * @return {@code object} if it is not {@code null}, defaultValue otherwise + */ + public static T defaultIfNull(T object, T defaultValue) { + return object != null ? object : defaultValue; + } + + /** + *

Returns the first value in the array which is not {@code null}. + * If all the values are {@code null} or the array is {@code null} + * or empty then {@code null} is returned.

+ * + *
+     * ObjectUtils.firstNonNull(null, null)      = null
+     * ObjectUtils.firstNonNull(null, "")        = ""
+     * ObjectUtils.firstNonNull(null, null, "")  = ""
+     * ObjectUtils.firstNonNull(null, "zz")      = "zz"
+     * ObjectUtils.firstNonNull("abc", *)        = "abc"
+     * ObjectUtils.firstNonNull(null, "xyz", *)  = "xyz"
+     * ObjectUtils.firstNonNull(Boolean.TRUE, *) = Boolean.TRUE
+     * ObjectUtils.firstNonNull()                = null
+     * 
+ * + * @param the component type of the array + * @param values the values to test, may be {@code null} or empty + * @return the first value from {@code values} which is not {@code null}, + * or {@code null} if there are no non-null values + * @since 3.0 + */ + public static T firstNonNull(T... values) { + if (values != null) { + for (T val : values) { + if (val != null) { + return val; + } + } + } + return null; + } + + // Null-safe equals/hashCode + //----------------------------------------------------------------------- + /** + *

Compares two objects for equality, where either one or both + * objects may be {@code null}.

+ * + *
+     * ObjectUtils.equals(null, null)                  = true
+     * ObjectUtils.equals(null, "")                    = false
+     * ObjectUtils.equals("", null)                    = false
+     * ObjectUtils.equals("", "")                      = true
+     * ObjectUtils.equals(Boolean.TRUE, null)          = false
+     * ObjectUtils.equals(Boolean.TRUE, "true")        = false
+     * ObjectUtils.equals(Boolean.TRUE, Boolean.TRUE)  = true
+     * ObjectUtils.equals(Boolean.TRUE, Boolean.FALSE) = false
+     * 
+ * + * @param object1 the first object, may be {@code null} + * @param object2 the second object, may be {@code null} + * @return {@code true} if the values of both objects are the same + */ + public static boolean equals(Object object1, Object object2) { + if (object1 == object2) { + return true; + } + if (object1 == null || object2 == null) { + return false; + } + return object1.equals(object2); + } + + /** + *

Compares two objects for inequality, where either one or both + * objects may be {@code null}.

+ * + *
+     * ObjectUtils.notEqual(null, null)                  = false
+     * ObjectUtils.notEqual(null, "")                    = true
+     * ObjectUtils.notEqual("", null)                    = true
+     * ObjectUtils.notEqual("", "")                      = false
+     * ObjectUtils.notEqual(Boolean.TRUE, null)          = true
+     * ObjectUtils.notEqual(Boolean.TRUE, "true")        = true
+     * ObjectUtils.notEqual(Boolean.TRUE, Boolean.TRUE)  = false
+     * ObjectUtils.notEqual(Boolean.TRUE, Boolean.FALSE) = true
+     * 
+ * + * @param object1 the first object, may be {@code null} + * @param object2 the second object, may be {@code null} + * @return {@code false} if the values of both objects are the same + */ + public static boolean notEqual(Object object1, Object object2) { + return ObjectUtils.equals(object1, object2) == false; + } + + /** + *

Gets the hash code of an object returning zero when the + * object is {@code null}.

+ * + *
+     * ObjectUtils.hashCode(null)   = 0
+     * ObjectUtils.hashCode(obj)    = obj.hashCode()
+     * 
+ * + * @param obj the object to obtain the hash code of, may be {@code null} + * @return the hash code of the object, or zero if null + * @since 2.1 + */ + public static int hashCode(Object obj) { + // hashCode(Object) retained for performance, as hash code is often critical + return obj == null ? 0 : obj.hashCode(); + } + + /** + *

Gets the hash code for multiple objects.

+ * + *

This allows a hash code to be rapidly calculated for a number of objects. + * The hash code for a single object is the not same as {@link #hashCode(Object)}. + * The hash code for multiple objects is the same as that calculated by an + * {@code ArrayList} containing the specified objects.

+ * + *
+     * ObjectUtils.hashCodeMulti()                 = 1
+     * ObjectUtils.hashCodeMulti((Object[]) null)  = 1
+     * ObjectUtils.hashCodeMulti(a)                = 31 + a.hashCode()
+     * ObjectUtils.hashCodeMulti(a,b)              = (31 + a.hashCode()) * 31 + b.hashCode()
+     * ObjectUtils.hashCodeMulti(a,b,c)            = ((31 + a.hashCode()) * 31 + b.hashCode()) * 31 + c.hashCode()
+     * 
+ * + * @param objects the objects to obtain the hash code of, may be {@code null} + * @return the hash code of the objects, or zero if null + * @since 3.0 + */ + public static int hashCodeMulti(Object... objects) { + int hash = 1; + if (objects != null) { + for (Object object : objects) { + hash = hash * 31 + ObjectUtils.hashCode(object); + } + } + return hash; + } + + // Identity ToString + //----------------------------------------------------------------------- + /** + *

Gets the toString that would be produced by {@code Object} + * if a class did not override toString itself. {@code null} + * will return {@code null}.

+ * + *
+     * ObjectUtils.identityToString(null)         = null
+     * ObjectUtils.identityToString("")           = "java.lang.String@1e23"
+     * ObjectUtils.identityToString(Boolean.TRUE) = "java.lang.Boolean@7fa"
+     * 
+ * + * @param object the object to create a toString for, may be + * {@code null} + * @return the default toString text, or {@code null} if + * {@code null} passed in + */ + public static String identityToString(Object object) { + if (object == null) { + return null; + } + StringBuffer buffer = new StringBuffer(); + identityToString(buffer, object); + return buffer.toString(); + } + + /** + *

Appends the toString that would be produced by {@code Object} + * if a class did not override toString itself. {@code null} + * will throw a NullPointerException for either of the two parameters.

+ * + *
+     * ObjectUtils.identityToString(buf, "")            = buf.append("java.lang.String@1e23"
+     * ObjectUtils.identityToString(buf, Boolean.TRUE)  = buf.append("java.lang.Boolean@7fa"
+     * ObjectUtils.identityToString(buf, Boolean.TRUE)  = buf.append("java.lang.Boolean@7fa")
+     * 
+ * + * @param buffer the buffer to append to + * @param object the object to create a toString for + * @since 2.4 + */ + public static void identityToString(StringBuffer buffer, Object object) { + if (object == null) { + throw new NullPointerException("Cannot get the toString of a null identity"); + } + buffer.append(object.getClass().getName()) + .append('@') + .append(Integer.toHexString(System.identityHashCode(object))); + } + + // ToString + //----------------------------------------------------------------------- + /** + *

Gets the {@code toString} of an {@code Object} returning + * an empty string ("") if {@code null} input.

+ * + *
+     * ObjectUtils.toString(null)         = ""
+     * ObjectUtils.toString("")           = ""
+     * ObjectUtils.toString("bat")        = "bat"
+     * ObjectUtils.toString(Boolean.TRUE) = "true"
+     * 
+ * + * @see StringUtils#defaultString(String) + * @see String#valueOf(Object) + * @param obj the Object to {@code toString}, may be null + * @return the passed in Object's toString, or nullStr if {@code null} input + * @since 2.0 + */ + public static String toString(Object obj) { + return obj == null ? "" : obj.toString(); + } + + /** + *

Gets the {@code toString} of an {@code Object} returning + * a specified text if {@code null} input.

+ * + *
+     * ObjectUtils.toString(null, null)           = null
+     * ObjectUtils.toString(null, "null")         = "null"
+     * ObjectUtils.toString("", "null")           = ""
+     * ObjectUtils.toString("bat", "null")        = "bat"
+     * ObjectUtils.toString(Boolean.TRUE, "null") = "true"
+     * 
+ * + * @see StringUtils#defaultString(String,String) + * @see String#valueOf(Object) + * @param obj the Object to {@code toString}, may be null + * @param nullStr the String to return if {@code null} input, may be null + * @return the passed in Object's toString, or nullStr if {@code null} input + * @since 2.0 + */ + public static String toString(Object obj, String nullStr) { + return obj == null ? nullStr : obj.toString(); + } + + // Comparable + //----------------------------------------------------------------------- + /** + *

Null safe comparison of Comparables.

+ * + * @param type of the values processed by this method + * @param values the set of comparable values, may be null + * @return + *
    + *
  • If any objects are non-null and unequal, the lesser object. + *
  • If all objects are non-null and equal, the first. + *
  • If any of the comparables are null, the lesser of the non-null objects. + *
  • If all the comparables are null, null is returned. + *
+ */ + public static > T min(T... values) { + T result = null; + if (values != null) { + for (T value : values) { + if (compare(value, result, true) < 0) { + result = value; + } + } + } + return result; + } + + /** + *

Null safe comparison of Comparables.

+ * + * @param type of the values processed by this method + * @param values the set of comparable values, may be null + * @return + *
    + *
  • If any objects are non-null and unequal, the greater object. + *
  • If all objects are non-null and equal, the first. + *
  • If any of the comparables are null, the greater of the non-null objects. + *
  • If all the comparables are null, null is returned. + *
+ */ + public static > T max(T... values) { + T result = null; + if (values != null) { + for (T value : values) { + if (compare(value, result, false) > 0) { + result = value; + } + } + } + return result; + } + + /** + *

Null safe comparison of Comparables. + * {@code null} is assumed to be less than a non-{@code null} value.

+ * + * @param type of the values processed by this method + * @param c1 the first comparable, may be null + * @param c2 the second comparable, may be null + * @return a negative value if c1 < c2, zero if c1 = c2 + * and a positive value if c1 > c2 + */ + public static > int compare(T c1, T c2) { + return compare(c1, c2, false); + } + + /** + *

Null safe comparison of Comparables.

+ * + * @param type of the values processed by this method + * @param c1 the first comparable, may be null + * @param c2 the second comparable, may be null + * @param nullGreater if true {@code null} is considered greater + * than a non-{@code null} value or if false {@code null} is + * considered less than a Non-{@code null} value + * @return a negative value if c1 < c2, zero if c1 = c2 + * and a positive value if c1 > c2 + * @see java.util.Comparator#compare(Object, Object) + */ + public static > int compare(T c1, T c2, boolean nullGreater) { + if (c1 == c2) { + return 0; + } else if (c1 == null) { + return nullGreater ? 1 : -1; + } else if (c2 == null) { + return nullGreater ? -1 : 1; + } + return c1.compareTo(c2); + } + + /** + * Find the "best guess" middle value among comparables. If there is an even + * number of total values, the lower of the two middle values will be returned. + * @param type of values processed by this method + * @param items to compare + * @return T at middle position + * @throws NullPointerException if items is {@code null} + * @throws IllegalArgumentException if items is empty or contains {@code null} values + * @since 3.0.1 + */ + public static > T median(T... items) { + Validate.notEmpty(items); + Validate.noNullElements(items); + TreeSet sort = new TreeSet(); + Collections.addAll(sort, items); + @SuppressWarnings("unchecked") //we know all items added were T instances + T result = (T) sort.toArray()[(sort.size() - 1) / 2]; + return result; + } + + /** + * Find the "best guess" middle value among comparables. If there is an even + * number of total values, the lower of the two middle values will be returned. + * @param type of values processed by this method + * @param comparator to use for comparisons + * @param items to compare + * @return T at middle position + * @throws NullPointerException if items or comparator is {@code null} + * @throws IllegalArgumentException if items is empty or contains {@code null} values + * @since 3.0.1 + */ + public static T median(Comparator comparator, T... items) { + Validate.notEmpty(items, "null/empty items"); + Validate.noNullElements(items); + Validate.notNull(comparator, "null comparator"); + TreeSet sort = new TreeSet(comparator); + Collections.addAll(sort, items); + @SuppressWarnings("unchecked") //we know all items added were T instances + T result = (T) sort.toArray()[(sort.size() - 1) / 2]; + return result; + } + + // Mode + //----------------------------------------------------------------------- + /** + * Find the most frequently occurring item. + * + * @param type of values processed by this method + * @param items to check + * @return most populous T, {@code null} if non-unique or no items supplied + * @since 3.0.1 + */ + public static T mode(T... items) { + if (ArrayUtils.isNotEmpty(items)) { + HashMap occurrences = new HashMap(items.length); + for (T t : items) { + MutableInt count = occurrences.get(t); + if (count == null) { + occurrences.put(t, new MutableInt(1)); + } else { + count.increment(); + } + } + T result = null; + int max = 0; + for (Map.Entry e : occurrences.entrySet()) { + int cmp = e.getValue().intValue(); + if (cmp == max) { + result = null; + } else if (cmp > max) { + max = cmp; + result = e.getKey(); + } + } + return result; + } + return null; + } + + // cloning + //----------------------------------------------------------------------- + /** + *

Clone an object.

+ * + * @param the type of the object + * @param obj the object to clone, null returns null + * @return the clone if the object implements {@link Cloneable} otherwise {@code null} + * @throws CloneFailedException if the object is cloneable and the clone operation fails + * @since 3.0 + */ + public static T clone(final T obj) { + if (obj instanceof Cloneable) { + final Object result; + if (obj.getClass().isArray()) { + final Class componentType = obj.getClass().getComponentType(); + if (!componentType.isPrimitive()) { + result = ((Object[]) obj).clone(); + } else { + int length = Array.getLength(obj); + result = Array.newInstance(componentType, length); + while (length-- > 0) { + Array.set(result, length, Array.get(obj, length)); + } + } + } else { + try { + final Method clone = obj.getClass().getMethod("clone"); + result = clone.invoke(obj); + } catch (final NoSuchMethodException e) { + throw new CloneFailedException("Cloneable type " + + obj.getClass().getName() + + " has no clone method", e); + } catch (final IllegalAccessException e) { + throw new CloneFailedException("Cannot clone Cloneable type " + + obj.getClass().getName(), e); + } catch (final InvocationTargetException e) { + throw new CloneFailedException("Exception cloning Cloneable type " + + obj.getClass().getName(), e.getCause()); + } + } + @SuppressWarnings("unchecked") + final T checked = (T) result; + return checked; + } + + return null; + } + + /** + *

Clone an object if possible.

+ * + *

This method is similar to {@link #clone(Object)}, but will return the provided + * instance as the return value instead of {@code null} if the instance + * is not cloneable. This is more convenient if the caller uses different + * implementations (e.g. of a service) and some of the implementations do not allow concurrent + * processing or have state. In such cases the implementation can simply provide a proper + * clone implementation and the caller's code does not have to change.

+ * + * @param the type of the object + * @param obj the object to clone, null returns null + * @return the clone if the object implements {@link Cloneable} otherwise the object itself + * @throws CloneFailedException if the object is cloneable and the clone operation fails + * @since 3.0 + */ + public static T cloneIfPossible(final T obj) { + final T clone = clone(obj); + return clone == null ? obj : clone; + } + + // Null + //----------------------------------------------------------------------- + /** + *

Class used as a null placeholder where {@code null} + * has another meaning.

+ * + *

For example, in a {@code HashMap} the + * {@link java.util.HashMap#get(java.lang.Object)} method returns + * {@code null} if the {@code Map} contains {@code null} or if there is + * no matching key. The {@code Null} placeholder can be used to distinguish + * between these two cases.

+ * + *

Another example is {@code Hashtable}, where {@code null} + * cannot be stored.

+ */ + public static class Null implements Serializable { + /** + * Required for serialization support. Declare serialization compatibility with Commons Lang 1.0 + * + * @see java.io.Serializable + */ + private static final long serialVersionUID = 7092611880189329093L; + + /** + * Restricted constructor - singleton. + */ + Null() { + super(); + } + + /** + *

Ensure singleton.

+ * + * @return the singleton value + */ + private Object readResolve() { + return ObjectUtils.NULL; + } + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/ObjectUtilsTest.java b/ApacheCommonsLang/org/apache/commons/lang3/ObjectUtilsTest.java new file mode 100644 index 0000000..2b8ada3 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/ObjectUtilsTest.java @@ -0,0 +1,521 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Comparator; +import java.util.Date; +import java.util.List; + +import org.apache.commons.lang3.exception.CloneFailedException; +import org.apache.commons.lang3.mutable.MutableObject; +import org.junit.Test; + +/** + * Unit tests {@link org.apache.commons.lang3.ObjectUtils}. + * + * @version $Id: ObjectUtilsTest.java 1309977 2012-04-05 17:53:39Z ggregory $ + */ +public class ObjectUtilsTest { + private static final String FOO = "foo"; + private static final String BAR = "bar"; + + //----------------------------------------------------------------------- + @Test + public void testConstructor() { + assertNotNull(new ObjectUtils()); + Constructor[] cons = ObjectUtils.class.getDeclaredConstructors(); + assertEquals(1, cons.length); + assertTrue(Modifier.isPublic(cons[0].getModifiers())); + assertTrue(Modifier.isPublic(ObjectUtils.class.getModifiers())); + assertFalse(Modifier.isFinal(ObjectUtils.class.getModifiers())); + } + + //----------------------------------------------------------------------- + @Test + public void testIsNull() { + Object o = FOO; + Object dflt = BAR; + assertSame("dflt was not returned when o was null", dflt, ObjectUtils.defaultIfNull(null, dflt)); + assertSame("dflt was returned when o was not null", o, ObjectUtils.defaultIfNull(o, dflt)); + } + + @Test + public void testFirstNonNull() { + assertEquals(null, ObjectUtils.firstNonNull(null, null)); + assertEquals("", ObjectUtils.firstNonNull(null, "")); + String firstNonNullGenerics = ObjectUtils.firstNonNull(null, null, "123", "456"); + assertEquals("123", firstNonNullGenerics); + assertEquals("123", ObjectUtils.firstNonNull("123", null, "456", null)); + assertEquals(null, ObjectUtils.firstNonNull()); + assertSame(Boolean.TRUE, ObjectUtils.firstNonNull(Boolean.TRUE)); + assertNull(ObjectUtils.firstNonNull()); + assertNull(ObjectUtils.firstNonNull(null, null)); +// assertSame("123", ObjectUtils.firstNonNull(null, ObjectUtils.NULL, "123", "456")); +// assertSame("456", ObjectUtils.firstNonNull(ObjectUtils.NULL, "456", "123", null)); +// assertNull(ObjectUtils.firstNonNull(null, null, ObjectUtils.NULL)); + assertNull(ObjectUtils.firstNonNull((Object) null)); + assertNull(ObjectUtils.firstNonNull((Object[]) null)); + } + + //----------------------------------------------------------------------- + @Test + public void testEquals() { + assertTrue("ObjectUtils.equals(null, null) returned false", ObjectUtils.equals(null, null)); + assertTrue("ObjectUtils.equals(\"foo\", null) returned true", !ObjectUtils.equals(FOO, null)); + assertTrue("ObjectUtils.equals(null, \"bar\") returned true", !ObjectUtils.equals(null, BAR)); + assertTrue("ObjectUtils.equals(\"foo\", \"bar\") returned true", !ObjectUtils.equals(FOO, BAR)); + assertTrue("ObjectUtils.equals(\"foo\", \"foo\") returned false", ObjectUtils.equals(FOO, FOO)); + } + + @Test + public void testNotEqual() { + assertFalse("ObjectUtils.notEqual(null, null) returned false", ObjectUtils.notEqual(null, null)); + assertTrue("ObjectUtils.notEqual(\"foo\", null) returned true", ObjectUtils.notEqual(FOO, null)); + assertTrue("ObjectUtils.notEqual(null, \"bar\") returned true", ObjectUtils.notEqual(null, BAR)); + assertTrue("ObjectUtils.notEqual(\"foo\", \"bar\") returned true", ObjectUtils.notEqual(FOO, BAR)); + assertFalse("ObjectUtils.notEqual(\"foo\", \"foo\") returned false", ObjectUtils.notEqual(FOO, FOO)); + } + + @Test + public void testHashCode() { + assertEquals(0, ObjectUtils.hashCode(null)); + assertEquals("a".hashCode(), ObjectUtils.hashCode("a")); + } + + @Test + public void testHashCodeMulti_multiple_emptyArray() { + Object[] array = new Object[0]; + assertEquals(1, ObjectUtils.hashCodeMulti(array)); + } + + @Test + public void testHashCodeMulti_multiple_nullArray() { + Object[] array = null; + assertEquals(1, ObjectUtils.hashCodeMulti(array)); + } + + @Test + public void testHashCodeMulti_multiple_likeList() { + List list0 = new ArrayList(Arrays.asList()); + assertEquals(list0.hashCode(), ObjectUtils.hashCodeMulti()); + + List list1 = new ArrayList(Arrays.asList("a")); + assertEquals(list1.hashCode(), ObjectUtils.hashCodeMulti("a")); + + List list2 = new ArrayList(Arrays.asList("a", "b")); + assertEquals(list2.hashCode(), ObjectUtils.hashCodeMulti("a", "b")); + + List list3 = new ArrayList(Arrays.asList("a", "b", "c")); + assertEquals(list3.hashCode(), ObjectUtils.hashCodeMulti("a", "b", "c")); + } + +// /** +// * Show that java.util.Date and java.sql.Timestamp are apples and oranges. +// * Prompted by an email discussion. +// * +// * The behavior is different b/w Sun Java 1.3.1_10 and 1.4.2_03. +// */ +// public void testDateEqualsJava() { +// long now = 1076957313284L; // Feb 16, 2004 10:49... PST +// java.util.Date date = new java.util.Date(now); +// java.sql.Timestamp realTimestamp = new java.sql.Timestamp(now); +// java.util.Date timestamp = realTimestamp; +// // sanity check 1: +// assertEquals(284000000, realTimestamp.getNanos()); +// assertEquals(1076957313284L, date.getTime()); +// // +// // On Sun 1.3.1_10: +// //junit.framework.AssertionFailedError: expected:<1076957313284> but was:<1076957313000> +// // +// //assertEquals(1076957313284L, timestamp.getTime()); +// // +// //junit.framework.AssertionFailedError: expected:<1076957313284> but was:<1076957313000> +// // +// //assertEquals(1076957313284L, realTimestamp.getTime()); +// // sanity check 2: +// assertEquals(date.getDay(), realTimestamp.getDay()); +// assertEquals(date.getHours(), realTimestamp.getHours()); +// assertEquals(date.getMinutes(), realTimestamp.getMinutes()); +// assertEquals(date.getMonth(), realTimestamp.getMonth()); +// assertEquals(date.getSeconds(), realTimestamp.getSeconds()); +// assertEquals(date.getTimezoneOffset(), realTimestamp.getTimezoneOffset()); +// assertEquals(date.getYear(), realTimestamp.getYear()); +// // +// // Time values are == and equals() on Sun 1.4.2_03 but NOT on Sun 1.3.1_10: +// // +// //assertFalse("Sanity check failed: date.getTime() == timestamp.getTime()", date.getTime() == timestamp.getTime()); +// //assertFalse("Sanity check failed: timestamp.equals(date)", timestamp.equals(date)); +// //assertFalse("Sanity check failed: date.equals(timestamp)", date.equals(timestamp)); +// // real test: +// //assertFalse("java.util.Date and java.sql.Timestamp should be equal", ObjectUtils.equals(date, timestamp)); +// } + + @Test + public void testIdentityToString() { + assertEquals(null, ObjectUtils.identityToString(null)); + assertEquals( + "java.lang.String@" + Integer.toHexString(System.identityHashCode(FOO)), + ObjectUtils.identityToString(FOO)); + Integer i = Integer.valueOf(90); + String expected = "java.lang.Integer@" + Integer.toHexString(System.identityHashCode(i)); + assertEquals(expected, ObjectUtils.identityToString(i)); + StringBuffer buffer = new StringBuffer(); + ObjectUtils.identityToString(buffer, i); + assertEquals(expected, buffer.toString()); + + try { + ObjectUtils.identityToString(null, "tmp"); + fail("NullPointerException expected"); + } catch(NullPointerException npe) { + } + try { + ObjectUtils.identityToString(new StringBuffer(), null); + fail("NullPointerException expected"); + } catch(NullPointerException npe) { + } + } + + @Test + public void testToString_Object() { + assertEquals("", ObjectUtils.toString((Object) null) ); + assertEquals(Boolean.TRUE.toString(), ObjectUtils.toString(Boolean.TRUE) ); + } + + @Test + public void testToString_ObjectString() { + assertEquals(BAR, ObjectUtils.toString((Object) null, BAR) ); + assertEquals(Boolean.TRUE.toString(), ObjectUtils.toString(Boolean.TRUE, BAR) ); + } + + @SuppressWarnings("cast") // 1 OK, because we are checking for code change + @Test + public void testNull() { + assertNotNull(ObjectUtils.NULL); + // 1 Check that NULL really is a Null i.e. the definition has not been changed + assertTrue(ObjectUtils.NULL instanceof ObjectUtils.Null); + assertSame(ObjectUtils.NULL, SerializationUtils.clone(ObjectUtils.NULL)); + } + + @Test + public void testMax() { + Calendar calendar = Calendar.getInstance(); + Date nonNullComparable1 = calendar.getTime(); + Date nonNullComparable2 = calendar.getTime(); + String[] nullAray = null; + + calendar.set( Calendar.YEAR, calendar.get( Calendar.YEAR ) -1 ); + Date minComparable = calendar.getTime(); + + assertNotSame( nonNullComparable1, nonNullComparable2 ); + + assertNull(ObjectUtils.max( (String) null ) ); + assertNull(ObjectUtils.max( nullAray ) ); + assertSame( nonNullComparable1, ObjectUtils.max( null, nonNullComparable1 ) ); + assertSame( nonNullComparable1, ObjectUtils.max( nonNullComparable1, null ) ); + assertSame( nonNullComparable1, ObjectUtils.max( null, nonNullComparable1, null ) ); + assertSame( nonNullComparable1, ObjectUtils.max( nonNullComparable1, nonNullComparable2 ) ); + assertSame( nonNullComparable2, ObjectUtils.max( nonNullComparable2, nonNullComparable1 ) ); + assertSame( nonNullComparable1, ObjectUtils.max( nonNullComparable1, minComparable ) ); + assertSame( nonNullComparable1, ObjectUtils.max( minComparable, nonNullComparable1 ) ); + assertSame( nonNullComparable1, ObjectUtils.max( null, minComparable, null, nonNullComparable1 ) ); + + assertNull( ObjectUtils.max((String)null, (String)null) ); + } + + @Test + public void testMin() { + Calendar calendar = Calendar.getInstance(); + Date nonNullComparable1 = calendar.getTime(); + Date nonNullComparable2 = calendar.getTime(); + String[] nullAray = null; + + calendar.set( Calendar.YEAR, calendar.get( Calendar.YEAR ) -1 ); + Date minComparable = calendar.getTime(); + + assertNotSame( nonNullComparable1, nonNullComparable2 ); + + assertNull(ObjectUtils.min( (String) null ) ); + assertNull(ObjectUtils.min( nullAray ) ); + assertSame( nonNullComparable1, ObjectUtils.min( null, nonNullComparable1 ) ); + assertSame( nonNullComparable1, ObjectUtils.min( nonNullComparable1, null ) ); + assertSame( nonNullComparable1, ObjectUtils.min( null, nonNullComparable1, null ) ); + assertSame( nonNullComparable1, ObjectUtils.min( nonNullComparable1, nonNullComparable2 ) ); + assertSame( nonNullComparable2, ObjectUtils.min( nonNullComparable2, nonNullComparable1 ) ); + assertSame( minComparable, ObjectUtils.min( nonNullComparable1, minComparable ) ); + assertSame( minComparable, ObjectUtils.min( minComparable, nonNullComparable1 ) ); + assertSame( minComparable, ObjectUtils.min( null, nonNullComparable1, null, minComparable ) ); + + assertNull( ObjectUtils.min((String)null, (String)null) ); + } + + /** + * Tests {@link ObjectUtils#compare(Comparable, Comparable, boolean)}. + */ + @Test + public void testCompare() { + Integer one = Integer.valueOf(1); + Integer two = Integer.valueOf(2); + Integer nullValue = null; + + assertEquals("Null Null false", 0, ObjectUtils.compare(nullValue, nullValue)); + assertEquals("Null Null true", 0, ObjectUtils.compare(nullValue, nullValue, true)); + + assertEquals("Null one false", -1, ObjectUtils.compare(nullValue, one)); + assertEquals("Null one true", 1, ObjectUtils.compare(nullValue, one, true)); + + assertEquals("one Null false", 1, ObjectUtils.compare(one, nullValue)); + assertEquals("one Null true", -1, ObjectUtils.compare(one, nullValue, true)); + + assertEquals("one two false", -1, ObjectUtils.compare(one, two)); + assertEquals("one two true", -1, ObjectUtils.compare(one, two, true)); + } + + @Test + public void testMedian() { + assertEquals("foo", ObjectUtils.median("foo")); + assertEquals("bar", ObjectUtils.median("foo", "bar")); + assertEquals("baz", ObjectUtils.median("foo", "bar", "baz")); + assertEquals("baz", ObjectUtils.median("foo", "bar", "baz", "blah")); + assertEquals("blah", ObjectUtils.median("foo", "bar", "baz", "blah", "wah")); + assertEquals(Integer.valueOf(5), + ObjectUtils.median(Integer.valueOf(1), Integer.valueOf(5), Integer.valueOf(10))); + assertEquals( + Integer.valueOf(7), + ObjectUtils.median(Integer.valueOf(5), Integer.valueOf(6), Integer.valueOf(7), Integer.valueOf(8), + Integer.valueOf(9))); + assertEquals(Integer.valueOf(6), + ObjectUtils.median(Integer.valueOf(5), Integer.valueOf(6), Integer.valueOf(7), Integer.valueOf(8))); + } + + @Test(expected = NullPointerException.class) + public void testMedian_nullItems() { + ObjectUtils.median((String[]) null); + } + + @Test(expected = IllegalArgumentException.class) + public void testMedian_emptyItems() { + ObjectUtils. median(); + } + + @Test + public void testComparatorMedian() { + CharSequenceComparator cmp = new CharSequenceComparator(); + NonComparableCharSequence foo = new NonComparableCharSequence("foo"); + NonComparableCharSequence bar = new NonComparableCharSequence("bar"); + NonComparableCharSequence baz = new NonComparableCharSequence("baz"); + NonComparableCharSequence blah = new NonComparableCharSequence("blah"); + NonComparableCharSequence wah = new NonComparableCharSequence("wah"); + assertSame(foo, ObjectUtils.median(cmp, foo)); + assertSame(bar, ObjectUtils.median(cmp, foo, bar)); + assertSame(baz, ObjectUtils.median(cmp, foo, bar, baz)); + assertSame(baz, ObjectUtils.median(cmp, foo, bar, baz, blah)); + assertSame(blah, ObjectUtils.median(cmp, foo, bar, baz, blah, wah)); + } + + @Test(expected = NullPointerException.class) + public void testComparatorMedian_nullComparator() { + ObjectUtils.median((Comparator) null, new NonComparableCharSequence("foo")); + } + + @Test(expected = NullPointerException.class) + public void testComparatorMedian_nullItems() { + ObjectUtils.median(new CharSequenceComparator(), (CharSequence[]) null); + } + + @Test(expected = IllegalArgumentException.class) + public void testComparatorMedian_emptyItems() { + ObjectUtils.median(new CharSequenceComparator()); + } + + @SuppressWarnings("unchecked") + @Test + public void testMode() { + assertNull(ObjectUtils.mode((Object[]) null)); + assertNull(ObjectUtils.mode()); + assertNull(ObjectUtils.mode("foo", "bar", "baz")); + assertNull(ObjectUtils.mode("foo", "bar", "baz", "foo", "bar")); + assertEquals("foo", ObjectUtils.mode("foo", "bar", "baz", "foo")); + assertEquals(Integer.valueOf(9), + ObjectUtils.mode("foo", "bar", "baz", Integer.valueOf(9), Integer.valueOf(10), Integer.valueOf(9))); + } + + /** + * Tests {@link ObjectUtils#clone(Object)} with a cloneable object. + */ + @Test + public void testCloneOfCloneable() { + final CloneableString string = new CloneableString("apache"); + final CloneableString stringClone = ObjectUtils.clone(string); + assertEquals("apache", stringClone.getValue()); + } + + /** + * Tests {@link ObjectUtils#clone(Object)} with a not cloneable object. + */ + @Test + public void testCloneOfNotCloneable() { + final String string = new String("apache"); + assertNull(ObjectUtils.clone(string)); + } + + /** + * Tests {@link ObjectUtils#clone(Object)} with an uncloneable object. + */ + @Test(expected = NoSuchMethodException.class) + public void testCloneOfUncloneable() throws Throwable { + final UncloneableString string = new UncloneableString("apache"); + try { + ObjectUtils.clone(string); + fail("Thrown " + CloneFailedException.class.getName() + " expected"); + } catch (final CloneFailedException e) { + throw e.getCause(); + } + } + + /** + * Tests {@link ObjectUtils#clone(Object)} with an object array. + */ + @Test + public void testCloneOfStringArray() { + assertTrue(Arrays.deepEquals( + new String[]{"string"}, ObjectUtils.clone(new String[]{"string"}))); + } + + /** + * Tests {@link ObjectUtils#clone(Object)} with an array of primitives. + */ + @Test + public void testCloneOfPrimitiveArray() { + assertTrue(Arrays.equals(new int[]{1}, ObjectUtils.clone(new int[]{1}))); + } + + /** + * Tests {@link ObjectUtils#cloneIfPossible(Object)} with a cloneable object. + */ + @Test + public void testPossibleCloneOfCloneable() { + final CloneableString string = new CloneableString("apache"); + final CloneableString stringClone = ObjectUtils.cloneIfPossible(string); + assertEquals("apache", stringClone.getValue()); + } + + /** + * Tests {@link ObjectUtils#cloneIfPossible(Object)} with a not cloneable object. + */ + @Test + public void testPossibleCloneOfNotCloneable() { + final String string = new String("apache"); + assertSame(string, ObjectUtils.cloneIfPossible(string)); + } + + /** + * Tests {@link ObjectUtils#cloneIfPossible(Object)} with an uncloneable object. + */ + @Test(expected = NoSuchMethodException.class) + public void testPossibleCloneOfUncloneable() throws Throwable { + final UncloneableString string = new UncloneableString("apache"); + try { + ObjectUtils.cloneIfPossible(string); + fail("Thrown " + CloneFailedException.class.getName() + " expected"); + } catch (final CloneFailedException e) { + throw e.getCause(); + } + } + + /** + * String that is cloneable. + */ + static final class CloneableString extends MutableObject implements Cloneable { + private static final long serialVersionUID = 1L; + CloneableString(final String s) { + super(s); + } + + @Override + public CloneableString clone() throws CloneNotSupportedException { + return (CloneableString)super.clone(); + } + } + + /** + * String that is not cloneable. + */ + static final class UncloneableString extends MutableObject implements Cloneable { + private static final long serialVersionUID = 1L; + UncloneableString(final String s) { + super(s); + } + } + + static final class NonComparableCharSequence implements CharSequence { + final String value; + + /** + * Create a new NonComparableCharSequence instance. + * + * @param value + */ + public NonComparableCharSequence(String value) { + super(); + Validate.notNull(value); + this.value = value; + } + + @Override + public char charAt(int arg0) { + return value.charAt(arg0); + } + + @Override + public int length() { + return value.length(); + } + + @Override + public CharSequence subSequence(int arg0, int arg1) { + return value.subSequence(arg0, arg1); + } + + @Override + public String toString() { + return value; + } + } + + static final class CharSequenceComparator implements Comparator { + + @Override + public int compare(CharSequence o1, CharSequence o2) { + return o1.toString().compareTo(o2.toString()); + } + + } +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/RandomStringUtils.java b/ApacheCommonsLang/org/apache/commons/lang3/RandomStringUtils.java new file mode 100644 index 0000000..c9289ff --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/RandomStringUtils.java @@ -0,0 +1,335 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3; + +import java.util.Random; + +/** + *

Operations for random {@code String}s.

+ *

Currently private high surrogate characters are ignored. + * These are Unicode characters that fall between the values 56192 (db80) + * and 56319 (dbff) as we don't know how to handle them. + * High and low surrogates are correctly dealt with - that is if a + * high surrogate is randomly chosen, 55296 (d800) to 56191 (db7f) + * then it is followed by a low surrogate. If a low surrogate is chosen, + * 56320 (dc00) to 57343 (dfff) then it is placed after a randomly + * chosen high surrogate.

+ * + *

#ThreadSafe#

+ * @since 1.0 + * @version $Id: RandomStringUtils.java 1348583 2012-06-10 12:40:48Z sebb $ + */ +public class RandomStringUtils { + + /** + *

Random object used by random method. This has to be not local + * to the random method so as to not return the same value in the + * same millisecond.

+ */ + private static final Random RANDOM = new Random(); + + /** + *

{@code RandomStringUtils} instances should NOT be constructed in + * standard programming. Instead, the class should be used as + * {@code RandomStringUtils.random(5);}.

+ * + *

This constructor is public to permit tools that require a JavaBean instance + * to operate.

+ */ + public RandomStringUtils() { + super(); + } + + // Random + //----------------------------------------------------------------------- + /** + *

Creates a random string whose length is the number of characters + * specified.

+ * + *

Characters will be chosen from the set of all characters.

+ * + * @param count the length of random string to create + * @return the random string + */ + public static String random(int count) { + return random(count, false, false); + } + + /** + *

Creates a random string whose length is the number of characters + * specified.

+ * + *

Characters will be chosen from the set of characters whose + * ASCII value is between {@code 32} and {@code 126} (inclusive).

+ * + * @param count the length of random string to create + * @return the random string + */ + public static String randomAscii(int count) { + return random(count, 32, 127, false, false); + } + + /** + *

Creates a random string whose length is the number of characters + * specified.

+ * + *

Characters will be chosen from the set of alphabetic + * characters.

+ * + * @param count the length of random string to create + * @return the random string + */ + public static String randomAlphabetic(int count) { + return random(count, true, false); + } + + /** + *

Creates a random string whose length is the number of characters + * specified.

+ * + *

Characters will be chosen from the set of alpha-numeric + * characters.

+ * + * @param count the length of random string to create + * @return the random string + */ + public static String randomAlphanumeric(int count) { + return random(count, true, true); + } + + /** + *

Creates a random string whose length is the number of characters + * specified.

+ * + *

Characters will be chosen from the set of numeric + * characters.

+ * + * @param count the length of random string to create + * @return the random string + */ + public static String randomNumeric(int count) { + return random(count, false, true); + } + + /** + *

Creates a random string whose length is the number of characters + * specified.

+ * + *

Characters will be chosen from the set of alpha-numeric + * characters as indicated by the arguments.

+ * + * @param count the length of random string to create + * @param letters if {@code true}, generated string will include + * alphabetic characters + * @param numbers if {@code true}, generated string will include + * numeric characters + * @return the random string + */ + public static String random(int count, boolean letters, boolean numbers) { + return random(count, 0, 0, letters, numbers); + } + + /** + *

Creates a random string whose length is the number of characters + * specified.

+ * + *

Characters will be chosen from the set of alpha-numeric + * characters as indicated by the arguments.

+ * + * @param count the length of random string to create + * @param start the position in set of chars to start at + * @param end the position in set of chars to end before + * @param letters if {@code true}, generated string will include + * alphabetic characters + * @param numbers if {@code true}, generated string will include + * numeric characters + * @return the random string + */ + public static String random(int count, int start, int end, boolean letters, boolean numbers) { + return random(count, start, end, letters, numbers, null, RANDOM); + } + + /** + *

Creates a random string based on a variety of options, using + * default source of randomness.

+ * + *

This method has exactly the same semantics as + * {@link #random(int,int,int,boolean,boolean,char[],Random)}, but + * instead of using an externally supplied source of randomness, it uses + * the internal static {@link Random} instance.

+ * + * @param count the length of random string to create + * @param start the position in set of chars to start at + * @param end the position in set of chars to end before + * @param letters only allow letters? + * @param numbers only allow numbers? + * @param chars the set of chars to choose randoms from. + * If {@code null}, then it will use the set of all chars. + * @return the random string + * @throws ArrayIndexOutOfBoundsException if there are not + * {@code (end - start) + 1} characters in the set array. + */ + public static String random(int count, int start, int end, boolean letters, boolean numbers, char... chars) { + return random(count, start, end, letters, numbers, chars, RANDOM); + } + + /** + *

Creates a random string based on a variety of options, using + * supplied source of randomness.

+ * + *

If start and end are both {@code 0}, start and end are set + * to {@code ' '} and {@code 'z'}, the ASCII printable + * characters, will be used, unless letters and numbers are both + * {@code false}, in which case, start and end are set to + * {@code 0} and {@code Integer.MAX_VALUE}. + * + *

If set is not {@code null}, characters between start and + * end are chosen.

+ * + *

This method accepts a user-supplied {@link Random} + * instance to use as a source of randomness. By seeding a single + * {@link Random} instance with a fixed seed and using it for each call, + * the same random sequence of strings can be generated repeatedly + * and predictably.

+ * + * @param count the length of random string to create + * @param start the position in set of chars to start at + * @param end the position in set of chars to end before + * @param letters only allow letters? + * @param numbers only allow numbers? + * @param chars the set of chars to choose randoms from, must not be empty. + * If {@code null}, then it will use the set of all chars. + * @param random a source of randomness. + * @return the random string + * @throws ArrayIndexOutOfBoundsException if there are not + * {@code (end - start) + 1} characters in the set array. + * @throws IllegalArgumentException if {@code count} < 0 or the provided chars array is empty. + * @since 2.0 + */ + public static String random(int count, int start, int end, boolean letters, boolean numbers, + char[] chars, Random random) { + if (count == 0) { + return ""; + } else if (count < 0) { + throw new IllegalArgumentException("Requested random string length " + count + " is less than 0."); + } + if (chars != null && chars.length == 0) { + throw new IllegalArgumentException("The chars array must not be empty"); + } + + if (start == 0 && end == 0) { + if (chars != null) { + end = chars.length; + } else { + if (!letters && !numbers) { + end = Integer.MAX_VALUE; + } else { + end = 'z' + 1; + start = ' '; + } + } + } else { + if (end <= start) { + throw new IllegalArgumentException("Parameter end (" + end + ") must be greater than start (" + start + ")"); + } + } + + char[] buffer = new char[count]; + int gap = end - start; + + while (count-- != 0) { + char ch; + if (chars == null) { + ch = (char) (random.nextInt(gap) + start); + } else { + ch = chars[random.nextInt(gap) + start]; + } + if (letters && Character.isLetter(ch) + || numbers && Character.isDigit(ch) + || !letters && !numbers) { + if(ch >= 56320 && ch <= 57343) { + if(count == 0) { + count++; + } else { + // low surrogate, insert high surrogate after putting it in + buffer[count] = ch; + count--; + buffer[count] = (char) (55296 + random.nextInt(128)); + } + } else if(ch >= 55296 && ch <= 56191) { + if(count == 0) { + count++; + } else { + // high surrogate, insert low surrogate before putting it in + buffer[count] = (char) (56320 + random.nextInt(128)); + count--; + buffer[count] = ch; + } + } else if(ch >= 56192 && ch <= 56319) { + // private high surrogate, no effing clue, so skip it + count++; + } else { + buffer[count] = ch; + } + } else { + count++; + } + } + return new String(buffer); + } + + /** + *

Creates a random string whose length is the number of characters + * specified.

+ * + *

Characters will be chosen from the set of characters + * specified by the string, must not be empty. + * If null, the set of all characters is used.

+ * + * @param count the length of random string to create + * @param chars the String containing the set of characters to use, + * may be null, but must not be empty + * @return the random string + * @throws IllegalArgumentException if {@code count} < 0 or the string is empty. + */ + public static String random(int count, String chars) { + if (chars == null) { + return random(count, 0, 0, false, false, null, RANDOM); + } + return random(count, chars.toCharArray()); + } + + /** + *

Creates a random string whose length is the number of characters + * specified.

+ * + *

Characters will be chosen from the set of characters specified.

+ * + * @param count the length of random string to create + * @param chars the character array containing the set of characters to use, + * may be null + * @return the random string + * @throws IllegalArgumentException if {@code count} < 0. + */ + public static String random(int count, char... chars) { + if (chars == null) { + return random(count, 0, 0, false, false, null, RANDOM); + } + return random(count, 0, chars.length, false, false, chars, RANDOM); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/RandomStringUtilsTest.java b/ApacheCommonsLang/org/apache/commons/lang3/RandomStringUtilsTest.java new file mode 100644 index 0000000..060957f --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/RandomStringUtilsTest.java @@ -0,0 +1,348 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Modifier; +import java.util.Random; + +/** + * Unit tests {@link org.apache.commons.lang3.RandomStringUtils}. + * + * @version $Id: RandomStringUtilsTest.java 1348583 2012-06-10 12:40:48Z sebb $ + */ +public class RandomStringUtilsTest extends junit.framework.TestCase { + /** + * Construct a new instance of RandomStringUtilsTest with the specified name + */ + public RandomStringUtilsTest(String name) { + super(name); + } + + //----------------------------------------------------------------------- + public void testConstructor() { + assertNotNull(new RandomStringUtils()); + Constructor[] cons = RandomStringUtils.class.getDeclaredConstructors(); + assertEquals(1, cons.length); + assertEquals(true, Modifier.isPublic(cons[0].getModifiers())); + assertEquals(true, Modifier.isPublic(RandomStringUtils.class.getModifiers())); + assertEquals(false, Modifier.isFinal(RandomStringUtils.class.getModifiers())); + } + + //----------------------------------------------------------------------- + /** + * Test the implementation + */ + public void testRandomStringUtils() { + String r1 = RandomStringUtils.random(50); + assertEquals("random(50) length", 50, r1.length()); + String r2 = RandomStringUtils.random(50); + assertEquals("random(50) length", 50, r2.length()); + assertTrue("!r1.equals(r2)", !r1.equals(r2)); + + r1 = RandomStringUtils.randomAscii(50); + assertEquals("randomAscii(50) length", 50, r1.length()); + for(int i = 0; i < r1.length(); i++) { + assertTrue("char between 32 and 127", r1.charAt(i) >= 32 && r1.charAt(i) <= 127); + } + r2 = RandomStringUtils.randomAscii(50); + assertTrue("!r1.equals(r2)", !r1.equals(r2)); + + r1 = RandomStringUtils.randomAlphabetic(50); + assertEquals("randomAlphabetic(50)", 50, r1.length()); + for(int i = 0; i < r1.length(); i++) { + assertEquals("r1 contains alphabetic", true, Character.isLetter(r1.charAt(i)) && !Character.isDigit(r1.charAt(i))); + } + r2 = RandomStringUtils.randomAlphabetic(50); + assertTrue("!r1.equals(r2)", !r1.equals(r2)); + + r1 = RandomStringUtils.randomAlphanumeric(50); + assertEquals("randomAlphanumeric(50)", 50, r1.length()); + for(int i = 0; i < r1.length(); i++) { + assertEquals("r1 contains alphanumeric", true, Character.isLetterOrDigit(r1.charAt(i))); + } + r2 = RandomStringUtils.randomAlphabetic(50); + assertTrue("!r1.equals(r2)", !r1.equals(r2)); + + r1 = RandomStringUtils.randomNumeric(50); + assertEquals("randomNumeric(50)", 50, r1.length()); + for(int i = 0; i < r1.length(); i++) { + assertEquals("r1 contains numeric", true, Character.isDigit(r1.charAt(i)) && !Character.isLetter(r1.charAt(i))); + } + r2 = RandomStringUtils.randomNumeric(50); + assertTrue("!r1.equals(r2)", !r1.equals(r2)); + + String set = "abcdefg"; + r1 = RandomStringUtils.random(50, set); + assertEquals("random(50, \"abcdefg\")", 50, r1.length()); + for(int i = 0; i < r1.length(); i++) { + assertTrue("random char in set", set.indexOf(r1.charAt(i)) > -1); + } + r2 = RandomStringUtils.random(50, set); + assertTrue("!r1.equals(r2)", !r1.equals(r2)); + + r1 = RandomStringUtils.random(50, (String) null); + assertEquals("random(50) length", 50, r1.length()); + r2 = RandomStringUtils.random(50, (String) null); + assertEquals("random(50) length", 50, r2.length()); + assertTrue("!r1.equals(r2)", !r1.equals(r2)); + + set = "stuvwxyz"; + r1 = RandomStringUtils.random(50, set.toCharArray()); + assertEquals("random(50, \"stuvwxyz\")", 50, r1.length()); + for(int i = 0; i < r1.length(); i++) { + assertTrue("random char in set", set.indexOf(r1.charAt(i)) > -1); + } + r2 = RandomStringUtils.random(50, set); + assertTrue("!r1.equals(r2)", !r1.equals(r2)); + + r1 = RandomStringUtils.random(50, (char[]) null); + assertEquals("random(50) length", 50, r1.length()); + r2 = RandomStringUtils.random(50, (char[]) null); + assertEquals("random(50) length", 50, r2.length()); + assertTrue("!r1.equals(r2)", !r1.equals(r2)); + + long seed = System.currentTimeMillis(); + r1 = RandomStringUtils.random(50,0,0,true,true,null,new Random(seed)); + r2 = RandomStringUtils.random(50,0,0,true,true,null,new Random(seed)); + assertEquals("r1.equals(r2)", r1, r2); + + r1 = RandomStringUtils.random(0); + assertEquals("random(0).equals(\"\")", "", r1); + } + + public void testLANG805() { + long seed = System.currentTimeMillis(); + assertEquals("aaa", RandomStringUtils.random(3,0,0,false,false,new char[]{'a'},new Random(seed))); + } + + public void testLANG807() { + try { + RandomStringUtils.random(3,5,5,false,false); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException ex) { // distinguish from Random#nextInt message + final String msg = ex.getMessage(); + assertTrue("Message (" + msg + ") must contain 'start'", msg.contains("start")); + assertTrue("Message (" + msg + ") must contain 'end'", msg.contains("end")); + } + } + + public void testExceptions() { + final char[] DUMMY = new char[]{'a'}; // valid char array + try { + RandomStringUtils.random(-1); + fail(); + } catch (IllegalArgumentException ex) {} + try { + RandomStringUtils.random(-1, true, true); + fail(); + } catch (IllegalArgumentException ex) {} + try { + RandomStringUtils.random(-1, DUMMY); + fail(); + } catch (IllegalArgumentException ex) {} + try { + RandomStringUtils.random(1, new char[0]); // must not provide empty array => IAE + fail(); + } catch (IllegalArgumentException ex) {} + try { + RandomStringUtils.random(-1, ""); + fail(); + } catch (IllegalArgumentException ex) {} + try { + RandomStringUtils.random(-1, (String)null); + fail(); + } catch (IllegalArgumentException ex) {} + try { + RandomStringUtils.random(-1, 'a', 'z', false, false); + fail(); + } catch (IllegalArgumentException ex) {} + try { + RandomStringUtils.random(-1, 'a', 'z', false, false, DUMMY); + fail(); + } catch (IllegalArgumentException ex) {} + try { + RandomStringUtils.random(-1, 'a', 'z', false, false, DUMMY, new Random()); + fail(); + } catch (IllegalArgumentException ex) {} + } + + /** + * Make sure boundary alphanumeric characters are generated by randomAlphaNumeric + * This test will fail randomly with probability = 6 * (61/62)**1000 ~ 5.2E-7 + */ + public void testRandomAlphaNumeric() { + char[] testChars = {'a', 'z', 'A', 'Z', '0', '9'}; + boolean[] found = {false, false, false, false, false, false}; + for (int i = 0; i < 100; i++) { + String randString = RandomStringUtils.randomAlphanumeric(10); + for (int j = 0; j < testChars.length; j++) { + if (randString.indexOf(testChars[j]) > 0) { + found[j] = true; + } + } + } + for (int i = 0; i < testChars.length; i++) { + if (!found[i]) { + fail("alphanumeric character not generated in 1000 attempts: " + + testChars[i] +" -- repeated failures indicate a problem "); + } + } + } + + /** + * Make sure '0' and '9' are generated by randomNumeric + * This test will fail randomly with probability = 2 * (9/10)**1000 ~ 3.5E-46 + */ + public void testRandomNumeric() { + char[] testChars = {'0','9'}; + boolean[] found = {false, false}; + for (int i = 0; i < 100; i++) { + String randString = RandomStringUtils.randomNumeric(10); + for (int j = 0; j < testChars.length; j++) { + if (randString.indexOf(testChars[j]) > 0) { + found[j] = true; + } + } + } + for (int i = 0; i < testChars.length; i++) { + if (!found[i]) { + fail("digit not generated in 1000 attempts: " + + testChars[i] +" -- repeated failures indicate a problem "); + } + } + } + + /** + * Make sure boundary alpha characters are generated by randomAlphabetic + * This test will fail randomly with probability = 4 * (51/52)**1000 ~ 1.58E-8 + */ + public void testRandomAlphabetic() { + char[] testChars = {'a', 'z', 'A', 'Z'}; + boolean[] found = {false, false, false, false}; + for (int i = 0; i < 100; i++) { + String randString = RandomStringUtils.randomAlphabetic(10); + for (int j = 0; j < testChars.length; j++) { + if (randString.indexOf(testChars[j]) > 0) { + found[j] = true; + } + } + } + for (int i = 0; i < testChars.length; i++) { + if (!found[i]) { + fail("alphanumeric character not generated in 1000 attempts: " + + testChars[i] +" -- repeated failures indicate a problem "); + } + } + } + + /** + * Make sure 32 and 127 are generated by randomNumeric + * This test will fail randomly with probability = 2*(95/96)**1000 ~ 5.7E-5 + */ + public void testRandomAscii() { + char[] testChars = {(char) 32, (char) 126}; + boolean[] found = {false, false}; + for (int i = 0; i < 100; i++) { + String randString = RandomStringUtils.randomAscii(10); + for (int j = 0; j < testChars.length; j++) { + if (randString.indexOf(testChars[j]) > 0) { + found[j] = true; + } + } + } + for (int i = 0; i < testChars.length; i++) { + if (!found[i]) { + fail("ascii character not generated in 1000 attempts: " + + (int) testChars[i] + + " -- repeated failures indicate a problem"); + } + } + } + + /** + * Test homogeneity of random strings generated -- + * i.e., test that characters show up with expected frequencies + * in generated strings. Will fail randomly about 1 in 1000 times. + * Repeated failures indicate a problem. + */ + public void testRandomStringUtilsHomog() { + String set = "abc"; + char[] chars = set.toCharArray(); + String gen = ""; + int[] counts = {0,0,0}; + int[] expected = {200,200,200}; + for (int i = 0; i< 100; i++) { + gen = RandomStringUtils.random(6,chars); + for (int j = 0; j < 6; j++) { + switch (gen.charAt(j)) { + case 'a': {counts[0]++; break;} + case 'b': {counts[1]++; break;} + case 'c': {counts[2]++; break;} + default: {fail("generated character not in set");} + } + } + } + // Perform chi-square test with df = 3-1 = 2, testing at .001 level + assertTrue("test homogeneity -- will fail about 1 in 1000 times", + chiSquare(expected,counts) < 13.82); + } + + /** + * Computes Chi-Square statistic given observed and expected counts + * @param observed array of observed frequency counts + * @param expected array of expected frequency counts + */ + private double chiSquare(int[] expected, int[] observed) { + double sumSq = 0.0d; + double dev = 0.0d; + for (int i = 0; i < observed.length; i++) { + dev = observed[i] - expected[i]; + sumSq += dev * dev / expected[i]; + } + return sumSq; + } + + /** + * Checks if the string got by {@link RandomStringUtils#random(int)} + * can be converted to UTF-8 and back without loss. + * + * @see LANG-100 + * + * @throws Exception + */ + public void testLang100() throws Exception { + int size = 5000; + String encoding = "UTF-8"; + String orig = RandomStringUtils.random(size); + byte[] bytes = orig.getBytes(encoding); + String copy = new String(bytes, encoding); + + // for a verbose compare: + for (int i=0; i < orig.length() && i < copy.length(); i++) { + char o = orig.charAt(i); + char c = copy.charAt(i); + assertEquals("differs at " + i + "(" + Integer.toHexString(new Character(o).hashCode()) + "," + + Integer.toHexString(new Character(c).hashCode()) + ")", o, c); + } + // compare length also + assertEquals(orig.length(), copy.length()); + // just to be complete + assertEquals(orig, copy); + } +} + diff --git a/ApacheCommonsLang/org/apache/commons/lang3/Range.java b/ApacheCommonsLang/org/apache/commons/lang3/Range.java new file mode 100644 index 0000000..a5ea935 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/Range.java @@ -0,0 +1,494 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3; + +import java.io.Serializable; +import java.util.Comparator; + +/** + *

An immutable range of objects from a minimum to maximum point inclusive.

+ * + *

The objects need to either be implementations of {@code Comparable} + * or you need to supply a {@code Comparator}.

+ * + *

#ThreadSafe# if the objects and comparator are thread-safe

+ * + * @since 3.0 + * @version $Id: Range.java 1309977 2012-04-05 17:53:39Z ggregory $ + */ +public final class Range implements Serializable { + + /** + * Serialization version. + * @see java.io.Serializable + */ + private static final long serialVersionUID = 1L; + + /** + * The ordering scheme used in this range. + */ + private final Comparator comparator; + /** + * The minimum value in this range (inclusive). + */ + private final T minimum; + /** + * The maximum value in this range (inclusive). + */ + private final T maximum; + /** + * Cached output hashCode (class is immutable). + */ + private transient int hashCode; + /** + * Cached output toString (class is immutable). + */ + private transient String toString; + + /** + *

Obtains a range using the specified element as both the minimum + * and maximum in this range.

+ * + *

The range uses the natural ordering of the elements to determine where + * values lie in the range.

+ * + * @param the type of the elements in this range + * @param element the value to use for this range, not null + * @return the range object, not null + * @throws IllegalArgumentException if the element is null + * @throws ClassCastException if the element is not {@code Comparable} + */ + public static > Range is(T element) { + return between(element, element, null); + } + + /** + *

Obtains a range using the specified element as both the minimum + * and maximum in this range.

+ * + *

The range uses the specified {@code Comparator} to determine where + * values lie in the range.

+ * + * @param the type of the elements in this range + * @param element the value to use for this range, must not be {@code null} + * @param comparator the comparator to be used, null for natural ordering + * @return the range object, not null + * @throws IllegalArgumentException if the element is null + * @throws ClassCastException if using natural ordering and the elements are not {@code Comparable} + */ + public static Range is(T element, Comparator comparator) { + return between(element, element, comparator); + } + + /** + *

Obtains a range with the specified minimum and maximum values (both inclusive).

+ * + *

The range uses the natural ordering of the elements to determine where + * values lie in the range.

+ * + *

The arguments may be passed in the order (min,max) or (max,min). + * The getMinimum and getMaximum methods will return the correct values.

+ * + * @param the type of the elements in this range + * @param fromInclusive the first value that defines the edge of the range, inclusive + * @param toInclusive the second value that defines the edge of the range, inclusive + * @return the range object, not null + * @throws IllegalArgumentException if either element is null + * @throws ClassCastException if the elements are not {@code Comparable} + */ + public static > Range between(T fromInclusive, T toInclusive) { + return between(fromInclusive, toInclusive, null); + } + + /** + *

Obtains a range with the specified minimum and maximum values (both inclusive).

+ * + *

The range uses the specified {@code Comparator} to determine where + * values lie in the range.

+ * + *

The arguments may be passed in the order (min,max) or (max,min). + * The getMinimum and getMaximum methods will return the correct values.

+ * + * @param the type of the elements in this range + * @param fromInclusive the first value that defines the edge of the range, inclusive + * @param toInclusive the second value that defines the edge of the range, inclusive + * @param comparator the comparator to be used, null for natural ordering + * @return the range object, not null + * @throws IllegalArgumentException if either element is null + * @throws ClassCastException if using natural ordering and the elements are not {@code Comparable} + */ + public static Range between(T fromInclusive, T toInclusive, Comparator comparator) { + return new Range(fromInclusive, toInclusive, comparator); + } + + /** + * Creates an instance. + * + * @param element1 the first element, not null + * @param element2 the second element, not null + * @param comparator the comparator to be used, null for natural ordering + */ + @SuppressWarnings("unchecked") + private Range(T element1, T element2, Comparator comparator) { + if (element1 == null || element2 == null) { + throw new IllegalArgumentException("Elements in a range must not be null: element1=" + + element1 + ", element2=" + element2); + } + if (comparator == null) { + comparator = ComparableComparator.INSTANCE; + } + if (comparator.compare(element1, element2) < 1) { + this.minimum = element1; + this.maximum = element2; + } else { + this.minimum = element2; + this.maximum = element1; + } + this.comparator = comparator; + } + + // Accessors + //-------------------------------------------------------------------- + + /** + *

Gets the minimum value in this range.

+ * + * @return the minimum value in this range, not null + */ + public T getMinimum() { + return minimum; + } + + /** + *

Gets the maximum value in this range.

+ * + * @return the maximum value in this range, not null + */ + public T getMaximum() { + return maximum; + } + + /** + *

Gets the comparator being used to determine if objects are within the range.

+ * + *

Natural ordering uses an internal comparator implementation, thus this + * method never returns null. See {@link #isNaturalOrdering()}.

+ * + * @return the comparator being used, not null + */ + public Comparator getComparator() { + return comparator; + } + + /** + *

Whether or not the Range is using the natural ordering of the elements.

+ * + *

Natural ordering uses an internal comparator implementation, thus this + * method is the only way to check if a null comparator was specified.

+ * + * @return true if using natural ordering + */ + public boolean isNaturalOrdering() { + return comparator == ComparableComparator.INSTANCE; + } + + // Element tests + //-------------------------------------------------------------------- + + /** + *

Checks whether the specified element occurs within this range.

+ * + * @param element the element to check for, null returns false + * @return true if the specified element occurs within this range + */ + public boolean contains(T element) { + if (element == null) { + return false; + } + return comparator.compare(element, minimum) > -1 && comparator.compare(element, maximum) < 1; + } + + /** + *

Checks whether this range is after the specified element.

+ * + * @param element the element to check for, null returns false + * @return true if this range is entirely after the specified element + */ + public boolean isAfter(T element) { + if (element == null) { + return false; + } + return comparator.compare(element, minimum) < 0; + } + + /** + *

Checks whether this range starts with the specified element.

+ * + * @param element the element to check for, null returns false + * @return true if the specified element occurs within this range + */ + public boolean isStartedBy(T element) { + if (element == null) { + return false; + } + return comparator.compare(element, minimum) == 0; + } + + /** + *

Checks whether this range starts with the specified element.

+ * + * @param element the element to check for, null returns false + * @return true if the specified element occurs within this range + */ + public boolean isEndedBy(T element) { + if (element == null) { + return false; + } + return comparator.compare(element, maximum) == 0; + } + + /** + *

Checks whether this range is before the specified element.

+ * + * @param element the element to check for, null returns false + * @return true if this range is entirely before the specified element + */ + public boolean isBefore(T element) { + if (element == null) { + return false; + } + return comparator.compare(element, maximum) > 0; + } + + /** + *

Checks where the specified element occurs relative to this range.

+ * + *

The API is reminiscent of the Comparable interface returning {@code -1} if + * the element is before the range, {@code 0} if contained within the range and + * {@code 1} if the element is after the range.

+ * + * @param element the element to check for, not null + * @return -1, 0 or +1 depending on the element's location relative to the range + */ + public int elementCompareTo(T element) { + if (element == null) { + // Comparable API says throw NPE on null + throw new NullPointerException("Element is null"); + } + if (isAfter(element)) { + return -1; + } else if (isBefore(element)) { + return 1; + } else { + return 0; + } + } + + // Range tests + //-------------------------------------------------------------------- + + /** + *

Checks whether this range contains all the elements of the specified range.

+ * + *

This method may fail if the ranges have two different comparators or element types.

+ * + * @param otherRange the range to check, null returns false + * @return true if this range contains the specified range + * @throws RuntimeException if ranges cannot be compared + */ + public boolean containsRange(Range otherRange) { + if (otherRange == null) { + return false; + } + return contains(otherRange.minimum) + && contains(otherRange.maximum); + } + + /** + *

Checks whether this range is completely after the specified range.

+ * + *

This method may fail if the ranges have two different comparators or element types.

+ * + * @param otherRange the range to check, null returns false + * @return true if this range is completely after the specified range + * @throws RuntimeException if ranges cannot be compared + */ + public boolean isAfterRange(Range otherRange) { + if (otherRange == null) { + return false; + } + return isAfter(otherRange.maximum); + } + + /** + *

Checks whether this range is overlapped by the specified range.

+ * + *

Two ranges overlap if there is at least one element in common.

+ * + *

This method may fail if the ranges have two different comparators or element types.

+ * + * @param otherRange the range to test, null returns false + * @return true if the specified range overlaps with this + * range; otherwise, {@code false} + * @throws RuntimeException if ranges cannot be compared + */ + public boolean isOverlappedBy(Range otherRange) { + if (otherRange == null) { + return false; + } + return otherRange.contains(minimum) + || otherRange.contains(maximum) + || contains(otherRange.minimum); + } + + /** + *

Checks whether this range is completely before the specified range.

+ * + *

This method may fail if the ranges have two different comparators or element types.

+ * + * @param otherRange the range to check, null returns false + * @return true if this range is completely before the specified range + * @throws RuntimeException if ranges cannot be compared + */ + public boolean isBeforeRange(Range otherRange) { + if (otherRange == null) { + return false; + } + return isBefore(otherRange.minimum); + } + + /** + * Calculate the intersection of {@code this} and an overlapping Range. + * @param other overlapping Range + * @return range representing the intersection of {@code this} and {@code other} ({@code this} if equal) + * @throws IllegalArgumentException if {@code other} does not overlap {@code this} + * @since 3.0.1 + */ + public Range intersectionWith(Range other) { + if (!this.isOverlappedBy(other)) { + throw new IllegalArgumentException(String.format( + "Cannot calculate intersection with non-overlapping range %s", other)); + } + if (this.equals(other)) { + return this; + } + T min = getComparator().compare(minimum, other.minimum) < 0 ? other.minimum : minimum; + T max = getComparator().compare(maximum, other.maximum) < 0 ? maximum : other.maximum; + return between(min, max, getComparator()); + } + + // Basics + //-------------------------------------------------------------------- + + /** + *

Compares this range to another object to test if they are equal.

. + * + *

To be equal, the minimum and maximum values must be equal, which + * ignores any differences in the comparator.

+ * + * @param obj the reference object with which to compare + * @return true if this object is equal + */ + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } else if (obj == null || obj.getClass() != getClass()) { + return false; + } else { + @SuppressWarnings("unchecked") // OK because we checked the class above + Range range = (Range) obj; + return minimum.equals(range.minimum) && + maximum.equals(range.maximum); + } + } + + /** + *

Gets a suitable hash code for the range.

+ * + * @return a hash code value for this object + */ + @Override + public int hashCode() { + int result = hashCode; + if (hashCode == 0) { + result = 17; + result = 37 * result + getClass().hashCode(); + result = 37 * result + minimum.hashCode(); + result = 37 * result + maximum.hashCode(); + hashCode = result; + } + return result; + } + + /** + *

Gets the range as a {@code String}.

+ * + *

The format of the String is '[min..max]'.

+ * + * @return the {@code String} representation of this range + */ + @Override + public String toString() { + String result = toString; + if (result == null) { + StringBuilder buf = new StringBuilder(32); + buf.append('['); + buf.append(minimum); + buf.append(".."); + buf.append(maximum); + buf.append(']'); + result = buf.toString(); + toString = result; + } + return result; + } + + /** + *

Formats the receiver using the given format.

+ * + *

This uses {@link java.util.Formattable} to perform the formatting. Three variables may + * be used to embed the minimum, maximum and comparator. + * Use {@code %1$s} for the minimum element, {@code %2$s} for the maximum element + * and {@code %3$s} for the comparator. + * The default format used by {@code toString()} is {@code [%1$s..%2$s]}.

+ * + * @param format the format string, optionally containing {@code %1$s}, {@code %2$s} and {@code %3$s}, not null + * @return the formatted string, not null + */ + public String toString(String format) { + return String.format(format, minimum, maximum, comparator); + } + + //----------------------------------------------------------------------- + @SuppressWarnings({"rawtypes", "unchecked"}) + private enum ComparableComparator implements Comparator { + INSTANCE; + /** + * Comparable based compare implementation. + * + * @param obj1 left hand side of comparison + * @param obj2 right hand side of comparison + * @return negative, 0, positive comparison value + */ + @Override + public int compare(Object obj1, Object obj2) { + return ((Comparable) obj1).compareTo(obj2); + } + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/RangeTest.java b/ApacheCommonsLang/org/apache/commons/lang3/RangeTest.java new file mode 100644 index 0000000..27076b4 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/RangeTest.java @@ -0,0 +1,380 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.lang3; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.util.Comparator; + +import org.junit.Before; +import org.junit.Test; + +/** + *

+ * Tests the methods in the {@link org.apache.commons.lang3.Range} class. + *

+ * + * @version $Id: RangeTest.java 1309977 2012-04-05 17:53:39Z ggregory $ + */ +@SuppressWarnings("boxing") +public class RangeTest { + + private Range byteRange; + private Range byteRange2; + private Range byteRange3; + + private Range intRange; + private Range longRange; + private Range floatRange; + private Range doubleRange; + + @SuppressWarnings("cast") // intRange + @Before + public void setUp() { + byteRange = Range.between((byte) 0, (byte) 5); + byteRange2 = Range.between((byte) 0, (byte) 5); + byteRange3 = Range.between((byte) 0, (byte) 10); + + intRange = Range.between((int) 10, (int) 20); + longRange = Range.between((long) 10, (long) 20); + floatRange = Range.between((float) 10, (float) 20); + doubleRange = Range.between((double) 10, (double) 20); + } + + //----------------------------------------------------------------------- + @SuppressWarnings({ "rawtypes", "unchecked" }) + @Test + public void testComparableConstructors() { + Comparable c = + new Comparable() { + @Override + public int compareTo(Object other) { + return 1; + } + }; + Range r1 = Range.is(c); + Range r2 = Range.between(c, c); + assertEquals(true, r1.isNaturalOrdering()); + assertEquals(true, r2.isNaturalOrdering()); + } + + @Test + public void testIsWithCompare(){ + Comparator c = new Comparator(){ + @Override + public int compare(Integer o1, Integer o2) { + return 0; // all integers are equal + } + }; + Range ri = Range.is(10); + assertFalse("should not contain null",ri.contains(null)); + assertTrue("should contain 10",ri.contains(10)); + assertFalse("should not contain 11",ri.contains(11)); + ri = Range.is(10,c); + assertFalse("should not contain null",ri.contains(null)); + assertTrue("should contain 10",ri.contains(10)); + assertTrue("should contain 11",ri.contains(11)); + } + + @Test + public void testBetweenWithCompare(){ + // TODO add tests with a better comparator + Comparator c = new Comparator(){ + @Override + public int compare(Integer o1, Integer o2) { + return 0; // all integers are equal + } + }; + Range rb = Range.between(-10,20); + assertFalse("should not contain null",rb.contains(null)); + assertTrue("should contain 10",rb.contains(10)); + assertTrue("should contain -10",rb.contains(-10)); + assertFalse("should not contain 21",rb.contains(21)); + assertFalse("should not contain -11",rb.contains(-11)); + rb = Range.between(-10,20,c); + assertFalse("should not contain null",rb.contains(null)); + assertTrue("should contain 10",rb.contains(10)); + assertTrue("should contain -10",rb.contains(-10)); + assertTrue("should contain 21",rb.contains(21)); + assertTrue("should contain -11",rb.contains(-11)); + } + + //----------------------------------------------------------------------- + @Test + public void testRangeOfChars() { + Range chars = Range.between('a', 'z'); + assertTrue(chars.contains('b')); + assertFalse(chars.contains('B')); + } + + //----------------------------------------------------------------------- + @Test + public void testEqualsObject() { + assertEquals(byteRange, byteRange); + assertEquals(byteRange, byteRange2); + assertEquals(byteRange2, byteRange2); + assertTrue(byteRange.equals(byteRange)); + assertTrue(byteRange2.equals(byteRange2)); + assertTrue(byteRange3.equals(byteRange3)); + assertFalse(byteRange2.equals(byteRange3)); + assertFalse(byteRange2.equals(null)); + assertFalse(byteRange2.equals("Ni!")); + } + + @Test + public void testHashCode() { + assertEquals(byteRange.hashCode(), byteRange2.hashCode()); + assertFalse(byteRange.hashCode() == byteRange3.hashCode()); + + assertEquals(intRange.hashCode(), intRange.hashCode()); + assertTrue(intRange.hashCode() != 0); + } + + @Test + public void testToString() { + assertNotNull(byteRange.toString()); + + String str = intRange.toString(); + assertEquals("[10..20]", str); + assertEquals("[-20..-10]", Range.between(-20, -10).toString()); + } + + @Test + public void testToStringFormat() { + String str = intRange.toString("From %1$s to %2$s"); + assertEquals("From 10 to 20", str); + } + + //----------------------------------------------------------------------- + @Test + public void testGetMinimum() { + assertEquals(10, (int) intRange.getMinimum()); + assertEquals(10L, (long) longRange.getMinimum()); + assertEquals(10f, floatRange.getMinimum(), 0.00001f); + assertEquals(10d, doubleRange.getMinimum(), 0.00001d); + } + + @Test + public void testGetMaximum() { + assertEquals(20, (int) intRange.getMaximum()); + assertEquals(20L, (long) longRange.getMaximum()); + assertEquals(20f, floatRange.getMaximum(), 0.00001f); + assertEquals(20d, doubleRange.getMaximum(), 0.00001d); + } + + @Test + public void testContains() { + assertFalse(intRange.contains(null)); + + assertFalse(intRange.contains(5)); + assertTrue(intRange.contains(10)); + assertTrue(intRange.contains(15)); + assertTrue(intRange.contains(20)); + assertFalse(intRange.contains(25)); + } + + @Test + public void testIsAfter() { + assertFalse(intRange.isAfter(null)); + + assertTrue(intRange.isAfter(5)); + assertFalse(intRange.isAfter(10)); + assertFalse(intRange.isAfter(15)); + assertFalse(intRange.isAfter(20)); + assertFalse(intRange.isAfter(25)); + } + + @Test + public void testIsStartedBy() { + assertFalse(intRange.isStartedBy(null)); + + assertFalse(intRange.isStartedBy(5)); + assertTrue(intRange.isStartedBy(10)); + assertFalse(intRange.isStartedBy(15)); + assertFalse(intRange.isStartedBy(20)); + assertFalse(intRange.isStartedBy(25)); + } + + @Test + public void testIsEndedBy() { + assertFalse(intRange.isEndedBy(null)); + + assertFalse(intRange.isEndedBy(5)); + assertFalse(intRange.isEndedBy(10)); + assertFalse(intRange.isEndedBy(15)); + assertTrue(intRange.isEndedBy(20)); + assertFalse(intRange.isEndedBy(25)); + } + + @Test + public void testIsBefore() { + assertFalse(intRange.isBefore(null)); + + assertFalse(intRange.isBefore(5)); + assertFalse(intRange.isBefore(10)); + assertFalse(intRange.isBefore(15)); + assertFalse(intRange.isBefore(20)); + assertTrue(intRange.isBefore(25)); + } + + @Test + public void testElementCompareTo() { + try { + intRange.elementCompareTo(null); + fail("NullPointerException should have been thrown"); + } catch(NullPointerException npe) { + // expected + } + + assertEquals(-1, intRange.elementCompareTo(5)); + assertEquals(0, intRange.elementCompareTo(10)); + assertEquals(0, intRange.elementCompareTo(15)); + assertEquals(0, intRange.elementCompareTo(20)); + assertEquals(1, intRange.elementCompareTo(25)); + } + + //----------------------------------------------------------------------- + @Test + public void testContainsRange() { + + // null handling + assertFalse(intRange.containsRange(null)); + + // easy inside range + assertTrue(intRange.containsRange(Range.between(12, 18))); + + // outside range on each side + assertFalse(intRange.containsRange(Range.between(32, 45))); + assertFalse(intRange.containsRange(Range.between(2, 8))); + + // equals range + assertTrue(intRange.containsRange(Range.between(10, 20))); + + // overlaps + assertFalse(intRange.containsRange(Range.between(9, 14))); + assertFalse(intRange.containsRange(Range.between(16, 21))); + + // touches lower boundary + assertTrue(intRange.containsRange(Range.between(10, 19))); + assertFalse(intRange.containsRange(Range.between(10, 21))); + + // touches upper boundary + assertTrue(intRange.containsRange(Range.between(11, 20))); + assertFalse(intRange.containsRange(Range.between(9, 20))); + + // negative + assertFalse(intRange.containsRange(Range.between(-11, -18))); + } + + @Test + public void testIsAfterRange() { + assertFalse(intRange.isAfterRange(null)); + + assertTrue(intRange.isAfterRange(Range.between(5, 9))); + + assertFalse(intRange.isAfterRange(Range.between(5, 10))); + assertFalse(intRange.isAfterRange(Range.between(5, 20))); + assertFalse(intRange.isAfterRange(Range.between(5, 25))); + assertFalse(intRange.isAfterRange(Range.between(15, 25))); + + assertFalse(intRange.isAfterRange(Range.between(21, 25))); + + assertFalse(intRange.isAfterRange(Range.between(10, 20))); + } + + @Test + public void testIsOverlappedBy() { + + // null handling + assertFalse(intRange.isOverlappedBy(null)); + + // easy inside range + assertTrue(intRange.isOverlappedBy(Range.between(12, 18))); + + // outside range on each side + assertFalse(intRange.isOverlappedBy(Range.between(32, 45))); + assertFalse(intRange.isOverlappedBy(Range.between(2, 8))); + + // equals range + assertTrue(intRange.isOverlappedBy(Range.between(10, 20))); + + // overlaps + assertTrue(intRange.isOverlappedBy(Range.between(9, 14))); + assertTrue(intRange.isOverlappedBy(Range.between(16, 21))); + + // touches lower boundary + assertTrue(intRange.isOverlappedBy(Range.between(10, 19))); + assertTrue(intRange.isOverlappedBy(Range.between(10, 21))); + + // touches upper boundary + assertTrue(intRange.isOverlappedBy(Range.between(11, 20))); + assertTrue(intRange.isOverlappedBy(Range.between(9, 20))); + + // negative + assertFalse(intRange.isOverlappedBy(Range.between(-11, -18))); + } + + @Test + public void testIsBeforeRange() { + assertFalse(intRange.isBeforeRange(null)); + + assertFalse(intRange.isBeforeRange(Range.between(5, 9))); + + assertFalse(intRange.isBeforeRange(Range.between(5, 10))); + assertFalse(intRange.isBeforeRange(Range.between(5, 20))); + assertFalse(intRange.isBeforeRange(Range.between(5, 25))); + assertFalse(intRange.isBeforeRange(Range.between(15, 25))); + + assertTrue(intRange.isBeforeRange(Range.between(21, 25))); + + assertFalse(intRange.isBeforeRange(Range.between(10, 20))); + } + + @Test + public void testIntersectionWith() { + assertSame(intRange, intRange.intersectionWith(intRange)); + assertSame(byteRange, byteRange.intersectionWith(byteRange)); + assertSame(longRange, longRange.intersectionWith(longRange)); + assertSame(floatRange, floatRange.intersectionWith(floatRange)); + assertSame(doubleRange, doubleRange.intersectionWith(doubleRange)); + + assertEquals(Range.between(10, 15), intRange.intersectionWith(Range.between(5, 15))); + } + + @Test(expected = IllegalArgumentException.class) + public void testIntersectionWithNull() { + intRange.intersectionWith(null); + } + + @Test(expected = IllegalArgumentException.class) + public void testIntersectionWithNonOverlapping() { + intRange.intersectionWith(Range.between(0, 9)); + } + + //----------------------------------------------------------------------- + @Test + public void testSerializing() { + SerializationUtils.clone(intRange); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/SerializationException.java b/ApacheCommonsLang/org/apache/commons/lang3/SerializationException.java new file mode 100644 index 0000000..fbb0d11 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/SerializationException.java @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3; + +/** + *

Exception thrown when the Serialization process fails.

+ * + *

The original error is wrapped within this one.

+ * + *

#NotThreadSafe# because Throwable is not thread-safe

+ * @since 1.0 + * @version $Id: SerializationException.java 1202710 2011-11-16 13:50:36Z ggregory $ + */ +public class SerializationException extends RuntimeException { + + /** + * Required for serialization support. + * + * @see java.io.Serializable + */ + private static final long serialVersionUID = 4029025366392702726L; + + /** + *

Constructs a new {@code SerializationException} without specified + * detail message.

+ */ + public SerializationException() { + super(); + } + + /** + *

Constructs a new {@code SerializationException} with specified + * detail message.

+ * + * @param msg The error message. + */ + public SerializationException(String msg) { + super(msg); + } + + /** + *

Constructs a new {@code SerializationException} with specified + * nested {@code Throwable}.

+ * + * @param cause The {@code Exception} or {@code Error} + * that caused this exception to be thrown. + */ + public SerializationException(Throwable cause) { + super(cause); + } + + /** + *

Constructs a new {@code SerializationException} with specified + * detail message and nested {@code Throwable}.

+ * + * @param msg The error message. + * @param cause The {@code Exception} or {@code Error} + * that caused this exception to be thrown. + */ + public SerializationException(String msg, Throwable cause) { + super(msg, cause); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/SerializationUtils.java b/ApacheCommonsLang/org/apache/commons/lang3/SerializationUtils.java new file mode 100644 index 0000000..dee2115 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/SerializationUtils.java @@ -0,0 +1,322 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.ObjectStreamClass; +import java.io.OutputStream; +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +/** + *

Assists with the serialization process and performs additional functionality based + * on serialization.

+ *

+ *

    + *
  • Deep clone using serialization + *
  • Serialize managing finally and IOException + *
  • Deserialize managing finally and IOException + *
+ * + *

This class throws exceptions for invalid {@code null} inputs. + * Each method documents its behaviour in more detail.

+ * + *

#ThreadSafe#

+ * @since 1.0 + * @version $Id: SerializationUtils.java 1310584 2012-04-06 20:57:05Z ggregory $ + */ +public class SerializationUtils { + + /** + *

SerializationUtils instances should NOT be constructed in standard programming. + * Instead, the class should be used as {@code SerializationUtils.clone(object)}.

+ * + *

This constructor is public to permit tools that require a JavaBean instance + * to operate.

+ * @since 2.0 + */ + public SerializationUtils() { + super(); + } + + // Clone + //----------------------------------------------------------------------- + /** + *

Deep clone an {@code Object} using serialization.

+ * + *

This is many times slower than writing clone methods by hand + * on all objects in your object graph. However, for complex object + * graphs, or for those that don't support deep cloning this can + * be a simple alternative implementation. Of course all the objects + * must be {@code Serializable}.

+ * + * @param the type of the object involved + * @param object the {@code Serializable} object to clone + * @return the cloned object + * @throws SerializationException (runtime) if the serialization fails + */ + public static T clone(T object) { + if (object == null) { + return null; + } + byte[] objectData = serialize(object); + ByteArrayInputStream bais = new ByteArrayInputStream(objectData); + + ClassLoaderAwareObjectInputStream in = null; + try { + // stream closed in the finally + in = new ClassLoaderAwareObjectInputStream(bais, object.getClass().getClassLoader()); + /* + * when we serialize and deserialize an object, + * it is reasonable to assume the deserialized object + * is of the same type as the original serialized object + */ + @SuppressWarnings("unchecked") // see above + T readObject = (T) in.readObject(); + return readObject; + + } catch (ClassNotFoundException ex) { + throw new SerializationException("ClassNotFoundException while reading cloned object data", ex); + } catch (IOException ex) { + throw new SerializationException("IOException while reading cloned object data", ex); + } finally { + try { + if (in != null) { + in.close(); + } + } catch (IOException ex) { + throw new SerializationException("IOException on closing cloned object data InputStream.", ex); + } + } + } + + // Serialize + //----------------------------------------------------------------------- + /** + *

Serializes an {@code Object} to the specified stream.

+ * + *

The stream will be closed once the object is written. + * This avoids the need for a finally clause, and maybe also exception + * handling, in the application code.

+ * + *

The stream passed in is not buffered internally within this method. + * This is the responsibility of your application if desired.

+ * + * @param obj the object to serialize to bytes, may be null + * @param outputStream the stream to write to, must not be null + * @throws IllegalArgumentException if {@code outputStream} is {@code null} + * @throws SerializationException (runtime) if the serialization fails + */ + public static void serialize(Serializable obj, OutputStream outputStream) { + if (outputStream == null) { + throw new IllegalArgumentException("The OutputStream must not be null"); + } + ObjectOutputStream out = null; + try { + // stream closed in the finally + out = new ObjectOutputStream(outputStream); + out.writeObject(obj); + + } catch (IOException ex) { + throw new SerializationException(ex); + } finally { + try { + if (out != null) { + out.close(); + } + } catch (IOException ex) { // NOPMD + // ignore close exception + } + } + } + + /** + *

Serializes an {@code Object} to a byte array for + * storage/serialization.

+ * + * @param obj the object to serialize to bytes + * @return a byte[] with the converted Serializable + * @throws SerializationException (runtime) if the serialization fails + */ + public static byte[] serialize(Serializable obj) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(512); + serialize(obj, baos); + return baos.toByteArray(); + } + + // Deserialize + //----------------------------------------------------------------------- + /** + *

+ * Deserializes an {@code Object} from the specified stream. + *

+ * + *

+ * The stream will be closed once the object is written. This avoids the need for a finally clause, and maybe also + * exception handling, in the application code. + *

+ * + *

+ * The stream passed in is not buffered internally within this method. This is the responsibility of your + * application if desired. + *

+ * + *

+ * If the call site incorrectly types the return value, a {@link ClassCastException} is thrown from the call site. + * Without Generics in this declaration, the call site must type cast and can cause the same ClassCastException. + * Note that in both cases, the ClassCastException is in the call site, not in this method. + *

+ * + * @param inputStream + * the serialized object input stream, must not be null + * @return the deserialized object + * @throws IllegalArgumentException + * if {@code inputStream} is {@code null} + * @throws SerializationException + * (runtime) if the serialization fails + */ + @SuppressWarnings("unchecked") + // Don't warn about "(T) deserialize" because we want the avoid type casting call sites. + public static T deserialize(InputStream inputStream) { + if (inputStream == null) { + throw new IllegalArgumentException("The InputStream must not be null"); + } + ObjectInputStream in = null; + try { + // stream closed in the finally + in = new ObjectInputStream(inputStream); + return (T) in.readObject(); + + } catch (ClassNotFoundException ex) { + throw new SerializationException(ex); + } catch (IOException ex) { + throw new SerializationException(ex); + } finally { + try { + if (in != null) { + in.close(); + } + } catch (IOException ex) { // NOPMD + // ignore close exception + } + } + } + + /** + *

+ * Deserializes a single {@code Object} from an array of bytes. + *

+ * + *

+ * If the call site incorrectly types the return value, a {@link ClassCastException} is thrown from the call site. + * Without Generics in this declaration, the call site must type cast and can cause the same ClassCastException. + * Note that in both cases, the ClassCastException is in the call site, not in this method. + *

+ * + * @param objectData + * the serialized object, must not be null + * @return the deserialized object + * @throws IllegalArgumentException + * if {@code objectData} is {@code null} + * @throws SerializationException + * (runtime) if the serialization fails + */ + @SuppressWarnings("unchecked") + // Don't warn about "(T) deserialize" because we want the avoid type casting call sites. + public static T deserialize(byte[] objectData) { + if (objectData == null) { + throw new IllegalArgumentException("The byte[] must not be null"); + } + return (T) deserialize(new ByteArrayInputStream(objectData)); + } + + /** + *

Custom specialization of the standard JDK {@link java.io.ObjectInputStream} + * that uses a custom ClassLoader to resolve a class. + * If the specified ClassLoader is not able to resolve the class, + * the context classloader of the current thread will be used. + * This way, the standard deserialization work also in web-application + * containers and application servers, no matter in which of the + * ClassLoader the particular class that encapsulates + * serialization/deserialization lives.

+ * + *

For more in-depth information about the problem for which this + * class here is a workaround, see the JIRA issue LANG-626.

+ */ + static class ClassLoaderAwareObjectInputStream extends ObjectInputStream { + private static final Map> primitiveTypes = + new HashMap>(); + private ClassLoader classLoader; + + /** + * Constructor. + * @param in The InputStream. + * @param classLoader classloader to use + * @throws IOException if an I/O error occurs while reading stream header. + * @see java.io.ObjectInputStream + */ + public ClassLoaderAwareObjectInputStream(InputStream in, ClassLoader classLoader) throws IOException { + super(in); + this.classLoader = classLoader; + + primitiveTypes.put("byte", byte.class); + primitiveTypes.put("short", short.class); + primitiveTypes.put("int", int.class); + primitiveTypes.put("long", long.class); + primitiveTypes.put("float", float.class); + primitiveTypes.put("double", double.class); + primitiveTypes.put("boolean", boolean.class); + primitiveTypes.put("char", char.class); + primitiveTypes.put("void", void.class); + } + + /** + * Overriden version that uses the parametrized ClassLoader or the ClassLoader + * of the current Thread to resolve the class. + * @param desc An instance of class ObjectStreamClass. + * @return A Class object corresponding to desc. + * @throws IOException Any of the usual Input/Output exceptions. + * @throws ClassNotFoundException If class of a serialized object cannot be found. + */ + @Override + protected Class resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException { + String name = desc.getName(); + try { + return Class.forName(name, false, classLoader); + } catch (ClassNotFoundException ex) { + try { + return Class.forName(name, false, Thread.currentThread().getContextClassLoader()); + } catch (ClassNotFoundException cnfe) { + Class cls = primitiveTypes.get(name); + if (cls != null) { + return cls; + } else { + throw cnfe; + } + } + } + } + + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/SerializationUtilsTest.java b/ApacheCommonsLang/org/apache/commons/lang3/SerializationUtilsTest.java new file mode 100644 index 0000000..5e32420 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/SerializationUtilsTest.java @@ -0,0 +1,425 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.OutputStream; +import java.io.Serializable; +import java.lang.reflect.Constructor; +import java.lang.reflect.Modifier; +import java.util.HashMap; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +/** + * Unit tests {@link org.apache.commons.lang3.SerializationUtils}. + * + * @version $Id: SerializationUtilsTest.java 1329691 2012-04-24 12:56:31Z ggregory $ + */ +public class SerializationUtilsTest { + + static final String CLASS_NOT_FOUND_MESSAGE = "ClassNotFoundSerialization.readObject fake exception"; + protected static final String SERIALIZE_IO_EXCEPTION_MESSAGE = "Anonymous OutputStream I/O exception"; + + private String iString; + private Integer iInteger; + private HashMap iMap; + + @Before + public void setUp() { + iString = "foo"; + iInteger = Integer.valueOf(7); + iMap = new HashMap(); + iMap.put("FOO", iString); + iMap.put("BAR", iInteger); + } + + //----------------------------------------------------------------------- + + @Test + public void testConstructor() { + assertNotNull(new SerializationUtils()); + Constructor[] cons = SerializationUtils.class.getDeclaredConstructors(); + assertEquals(1, cons.length); + assertEquals(true, Modifier.isPublic(cons[0].getModifiers())); + assertEquals(true, Modifier.isPublic(SerializationUtils.class.getModifiers())); + assertEquals(false, Modifier.isFinal(SerializationUtils.class.getModifiers())); + } + + @Test + public void testException() { + SerializationException serEx; + Exception ex = new Exception(); + + serEx = new SerializationException(); + assertSame(null, serEx.getMessage()); + assertSame(null, serEx.getCause()); + + serEx = new SerializationException("Message"); + assertSame("Message", serEx.getMessage()); + assertSame(null, serEx.getCause()); + + serEx = new SerializationException(ex); + assertEquals("java.lang.Exception", serEx.getMessage()); + assertSame(ex, serEx.getCause()); + + serEx = new SerializationException("Message", ex); + assertSame("Message", serEx.getMessage()); + assertSame(ex, serEx.getCause()); + } + + //----------------------------------------------------------------------- + + @Test + public void testSerializeStream() throws Exception { + ByteArrayOutputStream streamTest = new ByteArrayOutputStream(); + SerializationUtils.serialize(iMap, streamTest); + + ByteArrayOutputStream streamReal = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(streamReal); + oos.writeObject(iMap); + oos.flush(); + oos.close(); + + byte[] testBytes = streamTest.toByteArray(); + byte[] realBytes = streamReal.toByteArray(); + assertEquals(testBytes.length, realBytes.length); + for (int i = 0; i < realBytes.length; i++) { + assertEquals(realBytes[i], testBytes[i]); + } + } + + @Test + public void testSerializeStreamUnserializable() throws Exception { + ByteArrayOutputStream streamTest = new ByteArrayOutputStream(); + try { + iMap.put(new Object(), new Object()); + SerializationUtils.serialize(iMap, streamTest); + } catch (SerializationException ex) { + return; + } + fail(); + } + + @Test + public void testSerializeStreamNullObj() throws Exception { + ByteArrayOutputStream streamTest = new ByteArrayOutputStream(); + SerializationUtils.serialize(null, streamTest); + + ByteArrayOutputStream streamReal = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(streamReal); + oos.writeObject(null); + oos.flush(); + oos.close(); + + byte[] testBytes = streamTest.toByteArray(); + byte[] realBytes = streamReal.toByteArray(); + assertEquals(testBytes.length, realBytes.length); + for (int i = 0; i < realBytes.length; i++) { + assertEquals(realBytes[i], testBytes[i]); + } + } + + @Test + public void testSerializeStreamObjNull() throws Exception { + try { + SerializationUtils.serialize(iMap, null); + } catch (IllegalArgumentException ex) { + return; + } + fail(); + } + + @Test + public void testSerializeStreamNullNull() throws Exception { + try { + SerializationUtils.serialize(null, null); + } catch (IllegalArgumentException ex) { + return; + } + fail(); + } + + @Test + public void testSerializeIOException() throws Exception { + // forces an IOException when the ObjectOutputStream is created, to test not closing the stream + // in the finally block + OutputStream streamTest = new OutputStream() { + @Override + public void write(int arg0) throws IOException { + throw new IOException(SERIALIZE_IO_EXCEPTION_MESSAGE); + } + }; + try { + SerializationUtils.serialize(iMap, streamTest); + } + catch(SerializationException e) { + assertEquals("java.io.IOException: " + SERIALIZE_IO_EXCEPTION_MESSAGE, e.getMessage()); + } + } + + //----------------------------------------------------------------------- + + @Test + public void testDeserializeStream() throws Exception { + ByteArrayOutputStream streamReal = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(streamReal); + oos.writeObject(iMap); + oos.flush(); + oos.close(); + + ByteArrayInputStream inTest = new ByteArrayInputStream(streamReal.toByteArray()); + Object test = SerializationUtils.deserialize(inTest); + assertNotNull(test); + assertTrue(test instanceof HashMap); + assertTrue(test != iMap); + HashMap testMap = (HashMap) test; + assertEquals(iString, testMap.get("FOO")); + assertTrue(iString != testMap.get("FOO")); + assertEquals(iInteger, testMap.get("BAR")); + assertTrue(iInteger != testMap.get("BAR")); + assertEquals(iMap, testMap); + } + + @Test(expected=ClassCastException.class) + public void testDeserializeClassCastException() { + final String value = "Hello"; + byte[] serialized = SerializationUtils.serialize(value); + Assert.assertEquals(value, SerializationUtils.deserialize(serialized)); + // Causes ClassCastException in call site, not in SerializationUtils.deserialize + Integer i = SerializationUtils.deserialize(serialized); + } + + @Test + public void testDeserializeStreamOfNull() throws Exception { + ByteArrayOutputStream streamReal = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(streamReal); + oos.writeObject(null); + oos.flush(); + oos.close(); + + ByteArrayInputStream inTest = new ByteArrayInputStream(streamReal.toByteArray()); + Object test = SerializationUtils.deserialize(inTest); + assertNull(test); + } + + @Test + public void testDeserializeStreamNull() throws Exception { + try { + SerializationUtils.deserialize((InputStream) null); + } catch (IllegalArgumentException ex) { + return; + } + fail(); + } + + @Test + public void testDeserializeStreamBadStream() throws Exception { + try { + SerializationUtils.deserialize(new ByteArrayInputStream(new byte[0])); + } catch (SerializationException ex) { + return; + } + fail(); + } + + @Test + public void testDeserializeStreamClassNotFound() throws Exception { + ByteArrayOutputStream streamReal = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(streamReal); + oos.writeObject(new ClassNotFoundSerialization()); + oos.flush(); + oos.close(); + + ByteArrayInputStream inTest = new ByteArrayInputStream(streamReal.toByteArray()); + try { + @SuppressWarnings("unused") + Object test = SerializationUtils.deserialize(inTest); + } catch(SerializationException se) { + assertEquals("java.lang.ClassNotFoundException: " + CLASS_NOT_FOUND_MESSAGE, se.getMessage()); + } + } + + //----------------------------------------------------------------------- + + @Test + public void testSerializeBytes() throws Exception { + byte[] testBytes = SerializationUtils.serialize(iMap); + + ByteArrayOutputStream streamReal = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(streamReal); + oos.writeObject(iMap); + oos.flush(); + oos.close(); + + byte[] realBytes = streamReal.toByteArray(); + assertEquals(testBytes.length, realBytes.length); + for (int i = 0; i < realBytes.length; i++) { + assertEquals(realBytes[i], testBytes[i]); + } + } + + @Test + public void testSerializeBytesUnserializable() throws Exception { + try { + iMap.put(new Object(), new Object()); + SerializationUtils.serialize(iMap); + } catch (SerializationException ex) { + return; + } + fail(); + } + + @Test + public void testSerializeBytesNull() throws Exception { + byte[] testBytes = SerializationUtils.serialize(null); + + ByteArrayOutputStream streamReal = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(streamReal); + oos.writeObject(null); + oos.flush(); + oos.close(); + + byte[] realBytes = streamReal.toByteArray(); + assertEquals(testBytes.length, realBytes.length); + for (int i = 0; i < realBytes.length; i++) { + assertEquals(realBytes[i], testBytes[i]); + } + } + + //----------------------------------------------------------------------- + + @Test + public void testDeserializeBytes() throws Exception { + ByteArrayOutputStream streamReal = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(streamReal); + oos.writeObject(iMap); + oos.flush(); + oos.close(); + + Object test = SerializationUtils.deserialize(streamReal.toByteArray()); + assertNotNull(test); + assertTrue(test instanceof HashMap); + assertTrue(test != iMap); + HashMap testMap = (HashMap) test; + assertEquals(iString, testMap.get("FOO")); + assertTrue(iString != testMap.get("FOO")); + assertEquals(iInteger, testMap.get("BAR")); + assertTrue(iInteger != testMap.get("BAR")); + assertEquals(iMap, testMap); + } + + @Test + public void testDeserializeBytesOfNull() throws Exception { + ByteArrayOutputStream streamReal = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(streamReal); + oos.writeObject(null); + oos.flush(); + oos.close(); + + Object test = SerializationUtils.deserialize(streamReal.toByteArray()); + assertNull(test); + } + + @Test + public void testDeserializeBytesNull() throws Exception { + try { + SerializationUtils.deserialize((byte[]) null); + } catch (IllegalArgumentException ex) { + return; + } + fail(); + } + + @Test + public void testDeserializeBytesBadStream() throws Exception { + try { + SerializationUtils.deserialize(new byte[0]); + } catch (SerializationException ex) { + return; + } + fail(); + } + + //----------------------------------------------------------------------- + + @Test + public void testClone() throws Exception { + Object test = SerializationUtils.clone(iMap); + assertNotNull(test); + assertTrue(test instanceof HashMap); + assertTrue(test != iMap); + HashMap testMap = (HashMap) test; + assertEquals(iString, testMap.get("FOO")); + assertTrue(iString != testMap.get("FOO")); + assertEquals(iInteger, testMap.get("BAR")); + assertTrue(iInteger != testMap.get("BAR")); + assertEquals(iMap, testMap); + } + + @Test + public void testCloneNull() throws Exception { + Object test = SerializationUtils.clone(null); + assertNull(test); + } + + @Test + public void testCloneUnserializable() throws Exception { + try { + iMap.put(new Object(), new Object()); + SerializationUtils.clone(iMap); + } catch (SerializationException ex) { + return; + } + fail(); + } + + @Test + public void testPrimitiveTypeClassSerialization() { + Class[] primitiveTypes = { byte.class, short.class, int.class, long.class, float.class, double.class, + boolean.class, char.class, void.class }; + + for (Class primitiveType : primitiveTypes) { + Class clone = SerializationUtils.clone(primitiveType); + assertEquals(primitiveType, clone); + } + } + +} + +@SuppressWarnings("serial") +class ClassNotFoundSerialization implements Serializable +{ + + private void readObject(ObjectInputStream in) throws ClassNotFoundException { + throw new ClassNotFoundException(SerializationUtilsTest.CLASS_NOT_FOUND_MESSAGE); + } +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/StringEscapeUtils.java b/ApacheCommonsLang/org/apache/commons/lang3/StringEscapeUtils.java new file mode 100644 index 0000000..5da4e1e --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/StringEscapeUtils.java @@ -0,0 +1,585 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3; + +import java.io.IOException; +import java.io.Writer; + +import org.apache.commons.lang3.text.translate.AggregateTranslator; +import org.apache.commons.lang3.text.translate.CharSequenceTranslator; +import org.apache.commons.lang3.text.translate.EntityArrays; +import org.apache.commons.lang3.text.translate.LookupTranslator; +import org.apache.commons.lang3.text.translate.NumericEntityUnescaper; +import org.apache.commons.lang3.text.translate.OctalUnescaper; +import org.apache.commons.lang3.text.translate.UnicodeEscaper; +import org.apache.commons.lang3.text.translate.UnicodeUnescaper; + +/** + *

Escapes and unescapes {@code String}s for + * Java, Java Script, HTML and XML.

+ * + *

#ThreadSafe#

+ * @since 2.0 + * @version $Id: StringEscapeUtils.java 1148520 2011-07-19 20:53:23Z ggregory $ + */ +public class StringEscapeUtils { + + /* ESCAPE TRANSLATORS */ + + /** + * Translator object for escaping Java. + * + * While {@link #escapeJava(String)} is the expected method of use, this + * object allows the Java escaping functionality to be used + * as the foundation for a custom translator. + * + * @since 3.0 + */ + public static final CharSequenceTranslator ESCAPE_JAVA = + new LookupTranslator( + new String[][] { + {"\"", "\\\""}, + {"\\", "\\\\"}, + }).with( + new LookupTranslator(EntityArrays.JAVA_CTRL_CHARS_ESCAPE()) + ).with( + UnicodeEscaper.outsideOf(32, 0x7f) + ); + + /** + * Translator object for escaping EcmaScript/JavaScript. + * + * While {@link #escapeEcmaScript(String)} is the expected method of use, this + * object allows the EcmaScript escaping functionality to be used + * as the foundation for a custom translator. + * + * @since 3.0 + */ + public static final CharSequenceTranslator ESCAPE_ECMASCRIPT = + new AggregateTranslator( + new LookupTranslator( + new String[][] { + {"'", "\\'"}, + {"\"", "\\\""}, + {"\\", "\\\\"}, + {"/", "\\/"} + }), + new LookupTranslator(EntityArrays.JAVA_CTRL_CHARS_ESCAPE()), + UnicodeEscaper.outsideOf(32, 0x7f) + ); + + /** + * Translator object for escaping XML. + * + * While {@link #escapeXml(String)} is the expected method of use, this + * object allows the XML escaping functionality to be used + * as the foundation for a custom translator. + * + * @since 3.0 + */ + public static final CharSequenceTranslator ESCAPE_XML = + new AggregateTranslator( + new LookupTranslator(EntityArrays.BASIC_ESCAPE()), + new LookupTranslator(EntityArrays.APOS_ESCAPE()) + ); + + /** + * Translator object for escaping HTML version 3.0. + * + * While {@link #escapeHtml3(String)} is the expected method of use, this + * object allows the HTML escaping functionality to be used + * as the foundation for a custom translator. + * + * @since 3.0 + */ + public static final CharSequenceTranslator ESCAPE_HTML3 = + new AggregateTranslator( + new LookupTranslator(EntityArrays.BASIC_ESCAPE()), + new LookupTranslator(EntityArrays.ISO8859_1_ESCAPE()) + ); + + /** + * Translator object for escaping HTML version 4.0. + * + * While {@link #escapeHtml4(String)} is the expected method of use, this + * object allows the HTML escaping functionality to be used + * as the foundation for a custom translator. + * + * @since 3.0 + */ + public static final CharSequenceTranslator ESCAPE_HTML4 = + new AggregateTranslator( + new LookupTranslator(EntityArrays.BASIC_ESCAPE()), + new LookupTranslator(EntityArrays.ISO8859_1_ESCAPE()), + new LookupTranslator(EntityArrays.HTML40_EXTENDED_ESCAPE()) + ); + + /** + * Translator object for escaping individual Comma Separated Values. + * + * While {@link #escapeCsv(String)} is the expected method of use, this + * object allows the CSV escaping functionality to be used + * as the foundation for a custom translator. + * + * @since 3.0 + */ + public static final CharSequenceTranslator ESCAPE_CSV = new CsvEscaper(); + + // TODO: Create a parent class - 'SinglePassTranslator' ? + // It would handle the index checking + length returning, + // and could also have an optimization check method. + static class CsvEscaper extends CharSequenceTranslator { + + private static final char CSV_DELIMITER = ','; + private static final char CSV_QUOTE = '"'; + private static final String CSV_QUOTE_STR = String.valueOf(CSV_QUOTE); + private static final char[] CSV_SEARCH_CHARS = + new char[] {CSV_DELIMITER, CSV_QUOTE, CharUtils.CR, CharUtils.LF}; + + @Override + public int translate(CharSequence input, int index, Writer out) throws IOException { + + if(index != 0) { + throw new IllegalStateException("CsvEscaper should never reach the [1] index"); + } + + if (StringUtils.containsNone(input.toString(), CSV_SEARCH_CHARS)) { + out.write(input.toString()); + } else { + out.write(CSV_QUOTE); + out.write(StringUtils.replace(input.toString(), CSV_QUOTE_STR, CSV_QUOTE_STR + CSV_QUOTE_STR)); + out.write(CSV_QUOTE); + } + return input.length(); + } + } + + /* UNESCAPE TRANSLATORS */ + + /** + * Translator object for unescaping escaped Java. + * + * While {@link #unescapeJava(String)} is the expected method of use, this + * object allows the Java unescaping functionality to be used + * as the foundation for a custom translator. + * + * @since 3.0 + */ + // TODO: throw "illegal character: \92" as an Exception if a \ on the end of the Java (as per the compiler)? + public static final CharSequenceTranslator UNESCAPE_JAVA = + new AggregateTranslator( + new OctalUnescaper(), // .between('\1', '\377'), + new UnicodeUnescaper(), + new LookupTranslator(EntityArrays.JAVA_CTRL_CHARS_UNESCAPE()), + new LookupTranslator( + new String[][] { + {"\\\\", "\\"}, + {"\\\"", "\""}, + {"\\'", "'"}, + {"\\", ""} + }) + ); + + /** + * Translator object for unescaping escaped EcmaScript. + * + * While {@link #unescapeEcmaScript(String)} is the expected method of use, this + * object allows the EcmaScript unescaping functionality to be used + * as the foundation for a custom translator. + * + * @since 3.0 + */ + public static final CharSequenceTranslator UNESCAPE_ECMASCRIPT = UNESCAPE_JAVA; + + /** + * Translator object for unescaping escaped HTML 3.0. + * + * While {@link #unescapeHtml3(String)} is the expected method of use, this + * object allows the HTML unescaping functionality to be used + * as the foundation for a custom translator. + * + * @since 3.0 + */ + public static final CharSequenceTranslator UNESCAPE_HTML3 = + new AggregateTranslator( + new LookupTranslator(EntityArrays.BASIC_UNESCAPE()), + new LookupTranslator(EntityArrays.ISO8859_1_UNESCAPE()), + new NumericEntityUnescaper() + ); + + /** + * Translator object for unescaping escaped HTML 4.0. + * + * While {@link #unescapeHtml4(String)} is the expected method of use, this + * object allows the HTML unescaping functionality to be used + * as the foundation for a custom translator. + * + * @since 3.0 + */ + public static final CharSequenceTranslator UNESCAPE_HTML4 = + new AggregateTranslator( + new LookupTranslator(EntityArrays.BASIC_UNESCAPE()), + new LookupTranslator(EntityArrays.ISO8859_1_UNESCAPE()), + new LookupTranslator(EntityArrays.HTML40_EXTENDED_UNESCAPE()), + new NumericEntityUnescaper() + ); + + /** + * Translator object for unescaping escaped XML. + * + * While {@link #unescapeXml(String)} is the expected method of use, this + * object allows the XML unescaping functionality to be used + * as the foundation for a custom translator. + * + * @since 3.0 + */ + public static final CharSequenceTranslator UNESCAPE_XML = + new AggregateTranslator( + new LookupTranslator(EntityArrays.BASIC_UNESCAPE()), + new LookupTranslator(EntityArrays.APOS_UNESCAPE()), + new NumericEntityUnescaper() + ); + + /** + * Translator object for unescaping escaped Comma Separated Value entries. + * + * While {@link #unescapeCsv(String)} is the expected method of use, this + * object allows the CSV unescaping functionality to be used + * as the foundation for a custom translator. + * + * @since 3.0 + */ + public static final CharSequenceTranslator UNESCAPE_CSV = new CsvUnescaper(); + + static class CsvUnescaper extends CharSequenceTranslator { + + private static final char CSV_DELIMITER = ','; + private static final char CSV_QUOTE = '"'; + private static final String CSV_QUOTE_STR = String.valueOf(CSV_QUOTE); + private static final char[] CSV_SEARCH_CHARS = + new char[] {CSV_DELIMITER, CSV_QUOTE, CharUtils.CR, CharUtils.LF}; + + @Override + public int translate(CharSequence input, int index, Writer out) throws IOException { + + if(index != 0) { + throw new IllegalStateException("CsvUnescaper should never reach the [1] index"); + } + + if ( input.charAt(0) != CSV_QUOTE || input.charAt(input.length() - 1) != CSV_QUOTE ) { + out.write(input.toString()); + return input.length(); + } + + // strip quotes + String quoteless = input.subSequence(1, input.length() - 1).toString(); + + if ( StringUtils.containsAny(quoteless, CSV_SEARCH_CHARS) ) { + // deal with escaped quotes; ie) "" + out.write(StringUtils.replace(quoteless, CSV_QUOTE_STR + CSV_QUOTE_STR, CSV_QUOTE_STR)); + } else { + out.write(input.toString()); + } + return input.length(); + } + } + + /* Helper functions */ + + /** + *

{@code StringEscapeUtils} instances should NOT be constructed in + * standard programming.

+ * + *

Instead, the class should be used as: + *

StringEscapeUtils.escapeJava("foo");

+ * + *

This constructor is public to permit tools that require a JavaBean + * instance to operate.

+ */ + public StringEscapeUtils() { + super(); + } + + // Java and JavaScript + //-------------------------------------------------------------------------- + /** + *

Escapes the characters in a {@code String} using Java String rules.

+ * + *

Deals correctly with quotes and control-chars (tab, backslash, cr, ff, etc.)

+ * + *

So a tab becomes the characters {@code '\\'} and + * {@code 't'}.

+ * + *

The only difference between Java strings and JavaScript strings + * is that in JavaScript, a single quote and forward-slash (/) are escaped.

+ * + *

Example: + *

+     * input string: He didn't say, "Stop!"
+     * output string: He didn't say, \"Stop!\"
+     * 
+ *

+ * + * @param input String to escape values in, may be null + * @return String with escaped values, {@code null} if null string input + */ + public static final String escapeJava(String input) { + return ESCAPE_JAVA.translate(input); + } + + /** + *

Escapes the characters in a {@code String} using EcmaScript String rules.

+ *

Escapes any values it finds into their EcmaScript String form. + * Deals correctly with quotes and control-chars (tab, backslash, cr, ff, etc.)

+ * + *

So a tab becomes the characters {@code '\\'} and + * {@code 't'}.

+ * + *

The only difference between Java strings and EcmaScript strings + * is that in EcmaScript, a single quote and forward-slash (/) are escaped.

+ * + *

Note that EcmaScript is best known by the JavaScript and ActionScript dialects.

+ * + *

Example: + *

+     * input string: He didn't say, "Stop!"
+     * output string: He didn\'t say, \"Stop!\"
+     * 
+ *

+ * + * @param input String to escape values in, may be null + * @return String with escaped values, {@code null} if null string input + * + * @since 3.0 + */ + public static final String escapeEcmaScript(String input) { + return ESCAPE_ECMASCRIPT.translate(input); + } + + /** + *

Unescapes any Java literals found in the {@code String}. + * For example, it will turn a sequence of {@code '\'} and + * {@code 'n'} into a newline character, unless the {@code '\'} + * is preceded by another {@code '\'}.

+ * + * @param input the {@code String} to unescape, may be null + * @return a new unescaped {@code String}, {@code null} if null string input + */ + public static final String unescapeJava(String input) { + return UNESCAPE_JAVA.translate(input); + } + + /** + *

Unescapes any EcmaScript literals found in the {@code String}.

+ * + *

For example, it will turn a sequence of {@code '\'} and {@code 'n'} + * into a newline character, unless the {@code '\'} is preceded by another + * {@code '\'}.

+ * + * @see #unescapeJava(String) + * @param input the {@code String} to unescape, may be null + * @return A new unescaped {@code String}, {@code null} if null string input + * + * @since 3.0 + */ + public static final String unescapeEcmaScript(String input) { + return UNESCAPE_ECMASCRIPT.translate(input); + } + + // HTML and XML + //-------------------------------------------------------------------------- + /** + *

Escapes the characters in a {@code String} using HTML entities.

+ * + *

+ * For example: + *

+ *

"bread" & "butter"

+ * becomes: + *

+ * &quot;bread&quot; &amp; &quot;butter&quot;. + *

+ * + *

Supports all known HTML 4.0 entities, including funky accents. + * Note that the commonly used apostrophe escape character (&apos;) + * is not a legal entity and so is not supported).

+ * + * @param input the {@code String} to escape, may be null + * @return a new escaped {@code String}, {@code null} if null string input + * + * @see ISO Entities + * @see HTML 3.2 Character Entities for ISO Latin-1 + * @see HTML 4.0 Character entity references + * @see HTML 4.01 Character References + * @see HTML 4.01 Code positions + * + * @since 3.0 + */ + public static final String escapeHtml4(String input) { + return ESCAPE_HTML4.translate(input); + } + + /** + *

Escapes the characters in a {@code String} using HTML entities.

+ *

Supports only the HTML 3.0 entities.

+ * + * @param input the {@code String} to escape, may be null + * @return a new escaped {@code String}, {@code null} if null string input + * + * @since 3.0 + */ + public static final String escapeHtml3(String input) { + return ESCAPE_HTML3.translate(input); + } + + //----------------------------------------------------------------------- + /** + *

Unescapes a string containing entity escapes to a string + * containing the actual Unicode characters corresponding to the + * escapes. Supports HTML 4.0 entities.

+ * + *

For example, the string "&lt;Fran&ccedil;ais&gt;" + * will become "<Français>"

+ * + *

If an entity is unrecognized, it is left alone, and inserted + * verbatim into the result string. e.g. "&gt;&zzzz;x" will + * become ">&zzzz;x".

+ * + * @param input the {@code String} to unescape, may be null + * @return a new unescaped {@code String}, {@code null} if null string input + * + * @since 3.0 + */ + public static final String unescapeHtml4(String input) { + return UNESCAPE_HTML4.translate(input); + } + + /** + *

Unescapes a string containing entity escapes to a string + * containing the actual Unicode characters corresponding to the + * escapes. Supports only HTML 3.0 entities.

+ * + * @param input the {@code String} to unescape, may be null + * @return a new unescaped {@code String}, {@code null} if null string input + * + * @since 3.0 + */ + public static final String unescapeHtml3(String input) { + return UNESCAPE_HTML3.translate(input); + } + + //----------------------------------------------------------------------- + /** + *

Escapes the characters in a {@code String} using XML entities.

+ * + *

For example: "bread" & "butter" => + * &quot;bread&quot; &amp; &quot;butter&quot;. + *

+ * + *

Supports only the five basic XML entities (gt, lt, quot, amp, apos). + * Does not support DTDs or external entities.

+ * + *

Note that Unicode characters greater than 0x7f are as of 3.0, no longer + * escaped. If you still wish this functionality, you can achieve it + * via the following: + * {@code StringEscapeUtils.ESCAPE_XML.with( NumericEntityEscaper.between(0x7f, Integer.MAX_VALUE) );}

+ * + * @param input the {@code String} to escape, may be null + * @return a new escaped {@code String}, {@code null} if null string input + * @see #unescapeXml(java.lang.String) + */ + public static final String escapeXml(String input) { + return ESCAPE_XML.translate(input); + } + + + //----------------------------------------------------------------------- + /** + *

Unescapes a string containing XML entity escapes to a string + * containing the actual Unicode characters corresponding to the + * escapes.

+ * + *

Supports only the five basic XML entities (gt, lt, quot, amp, apos). + * Does not support DTDs or external entities.

+ * + *

Note that numerical \\u Unicode codes are unescaped to their respective + * Unicode characters. This may change in future releases.

+ * + * @param input the {@code String} to unescape, may be null + * @return a new unescaped {@code String}, {@code null} if null string input + * @see #escapeXml(String) + */ + public static final String unescapeXml(String input) { + return UNESCAPE_XML.translate(input); + } + + + //----------------------------------------------------------------------- + + /** + *

Returns a {@code String} value for a CSV column enclosed in double quotes, + * if required.

+ * + *

If the value contains a comma, newline or double quote, then the + * String value is returned enclosed in double quotes.

+ *

+ * + *

Any double quote characters in the value are escaped with another double quote.

+ * + *

If the value does not contain a comma, newline or double quote, then the + * String value is returned unchanged.

+ *

+ * + * see Wikipedia and + * RFC 4180. + * + * @param input the input CSV column String, may be null + * @return the input String, enclosed in double quotes if the value contains a comma, + * newline or double quote, {@code null} if null string input + * @since 2.4 + */ + public static final String escapeCsv(String input) { + return ESCAPE_CSV.translate(input); + } + + /** + *

Returns a {@code String} value for an unescaped CSV column.

+ * + *

If the value is enclosed in double quotes, and contains a comma, newline + * or double quote, then quotes are removed. + *

+ * + *

Any double quote escaped characters (a pair of double quotes) are unescaped + * to just one double quote.

+ * + *

If the value is not enclosed in double quotes, or is and does not contain a + * comma, newline or double quote, then the String value is returned unchanged.

+ *

+ * + * see Wikipedia and + * RFC 4180. + * + * @param input the input CSV column String, may be null + * @return the input String, with enclosing double quotes removed and embedded double + * quotes unescaped, {@code null} if null string input + * @since 2.4 + */ + public static final String unescapeCsv(String input) { + return UNESCAPE_CSV.translate(input); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/StringEscapeUtilsTest.java b/ApacheCommonsLang/org/apache/commons/lang3/StringEscapeUtilsTest.java new file mode 100644 index 0000000..0eb163f --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/StringEscapeUtilsTest.java @@ -0,0 +1,514 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.io.FileInputStream; +import java.io.IOException; +import java.io.StringWriter; +import java.lang.reflect.Constructor; +import java.lang.reflect.Modifier; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.text.translate.CharSequenceTranslator; +import org.apache.commons.lang3.text.translate.NumericEntityEscaper; +import org.junit.Test; + +/** + * Unit tests for {@link StringEscapeUtils}. + * + * @version $Id: StringEscapeUtilsTest.java 1299413 2012-03-11 17:57:40Z ggregory $ + */ +public class StringEscapeUtilsTest { + private final static String FOO = "foo"; + + @Test + public void testConstructor() { + assertNotNull(new StringEscapeUtils()); + Constructor[] cons = StringEscapeUtils.class.getDeclaredConstructors(); + assertEquals(1, cons.length); + assertTrue(Modifier.isPublic(cons[0].getModifiers())); + assertTrue(Modifier.isPublic(StringEscapeUtils.class.getModifiers())); + assertFalse(Modifier.isFinal(StringEscapeUtils.class.getModifiers())); + } + + @Test + public void testEscapeJava() throws IOException { + assertEquals(null, StringEscapeUtils.escapeJava(null)); + try { + StringEscapeUtils.ESCAPE_JAVA.translate(null, null); + fail(); + } catch (IOException ex) { + fail(); + } catch (IllegalArgumentException ex) { + } + try { + StringEscapeUtils.ESCAPE_JAVA.translate("", null); + fail(); + } catch (IOException ex) { + fail(); + } catch (IllegalArgumentException ex) { + } + + assertEscapeJava("empty string", "", ""); + assertEscapeJava(FOO, FOO); + assertEscapeJava("tab", "\\t", "\t"); + assertEscapeJava("backslash", "\\\\", "\\"); + assertEscapeJava("single quote should not be escaped", "'", "'"); + assertEscapeJava("\\\\\\b\\t\\r", "\\\b\t\r"); + assertEscapeJava("\\u1234", "\u1234"); + assertEscapeJava("\\u0234", "\u0234"); + assertEscapeJava("\\u00EF", "\u00ef"); + assertEscapeJava("\\u0001", "\u0001"); + assertEscapeJava("Should use capitalized Unicode hex", "\\uABCD", "\uabcd"); + + assertEscapeJava("He didn't say, \\\"stop!\\\"", + "He didn't say, \"stop!\""); + assertEscapeJava("non-breaking space", "This space is non-breaking:" + "\\u00A0", + "This space is non-breaking:\u00a0"); + assertEscapeJava("\\uABCD\\u1234\\u012C", + "\uABCD\u1234\u012C"); + } + + /** + * Tests https://issues.apache.org/jira/browse/LANG-421 + */ + @Test + public void testEscapeJavaWithSlash() { + final String input = "String with a slash (/) in it"; + + final String expected = input; + final String actual = StringEscapeUtils.escapeJava(input); + + /** + * In 2.4 StringEscapeUtils.escapeJava(String) escapes '/' characters, which are not a valid character to escape + * in a Java string. + */ + assertEquals(expected, actual); + } + + private void assertEscapeJava(String escaped, String original) throws IOException { + assertEscapeJava(null, escaped, original); + } + + private void assertEscapeJava(String message, String expected, String original) throws IOException { + String converted = StringEscapeUtils.escapeJava(original); + message = "escapeJava(String) failed" + (message == null ? "" : (": " + message)); + assertEquals(message, expected, converted); + + StringWriter writer = new StringWriter(); + StringEscapeUtils.ESCAPE_JAVA.translate(original, writer); + assertEquals(expected, writer.toString()); + } + + @Test + public void testUnescapeJava() throws IOException { + assertEquals(null, StringEscapeUtils.unescapeJava(null)); + try { + StringEscapeUtils.UNESCAPE_JAVA.translate(null, null); + fail(); + } catch (IOException ex) { + fail(); + } catch (IllegalArgumentException ex) { + } + try { + StringEscapeUtils.UNESCAPE_JAVA.translate("", null); + fail(); + } catch (IOException ex) { + fail(); + } catch (IllegalArgumentException ex) { + } + try { + StringEscapeUtils.unescapeJava("\\u02-3"); + fail(); + } catch (RuntimeException ex) { + } + + assertUnescapeJava("", ""); + assertUnescapeJava("test", "test"); + assertUnescapeJava("\ntest\b", "\\ntest\\b"); + assertUnescapeJava("\u123425foo\ntest\b", "\\u123425foo\\ntest\\b"); + assertUnescapeJava("'\foo\teste\r", "\\'\\foo\\teste\\r"); + assertUnescapeJava("", "\\"); + //foo + assertUnescapeJava("lowercase Unicode", "\uABCDx", "\\uabcdx"); + assertUnescapeJava("uppercase Unicode", "\uABCDx", "\\uABCDx"); + assertUnescapeJava("Unicode as final character", "\uABCD", "\\uabcd"); + } + + private void assertUnescapeJava(String unescaped, String original) throws IOException { + assertUnescapeJava(null, unescaped, original); + } + + private void assertUnescapeJava(String message, String unescaped, String original) throws IOException { + String expected = unescaped; + String actual = StringEscapeUtils.unescapeJava(original); + + assertEquals("unescape(String) failed" + + (message == null ? "" : (": " + message)) + + ": expected '" + StringEscapeUtils.escapeJava(expected) + + // we escape this so we can see it in the error message + "' actual '" + StringEscapeUtils.escapeJava(actual) + "'", + expected, actual); + + StringWriter writer = new StringWriter(); + StringEscapeUtils.UNESCAPE_JAVA.translate(original, writer); + assertEquals(unescaped, writer.toString()); + + } + + @Test + public void testEscapeEcmaScript() { + assertEquals(null, StringEscapeUtils.escapeEcmaScript(null)); + try { + StringEscapeUtils.ESCAPE_ECMASCRIPT.translate(null, null); + fail(); + } catch (IOException ex) { + fail(); + } catch (IllegalArgumentException ex) { + } + try { + StringEscapeUtils.ESCAPE_ECMASCRIPT.translate("", null); + fail(); + } catch (IOException ex) { + fail(); + } catch (IllegalArgumentException ex) { + } + + assertEquals("He didn\\'t say, \\\"stop!\\\"", StringEscapeUtils.escapeEcmaScript("He didn't say, \"stop!\"")); + assertEquals("document.getElementById(\\\"test\\\").value = \\'';")); + } + + + // HTML and XML + //-------------------------------------------------------------- + + private static final String[][] HTML_ESCAPES = { + {"no escaping", "plain text", "plain text"}, + {"no escaping", "plain text", "plain text"}, + {"empty string", "", ""}, + {"null", null, null}, + {"ampersand", "bread & butter", "bread & butter"}, + {"quotes", ""bread" & butter", "\"bread\" & butter"}, + {"final character only", "greater than >", "greater than >"}, + {"first character only", "< less than", "< less than"}, + {"apostrophe", "Huntington's chorea", "Huntington's chorea"}, + {"languages", "English,Français,\u65E5\u672C\u8A9E (nihongo)", "English,Fran\u00E7ais,\u65E5\u672C\u8A9E (nihongo)"}, + {"8-bit ascii shouldn't number-escape", "\u0080\u009F", "\u0080\u009F"}, + }; + + @Test + public void testEscapeHtml() { + for (int i = 0; i < HTML_ESCAPES.length; ++i) { + String message = HTML_ESCAPES[i][0]; + String expected = HTML_ESCAPES[i][1]; + String original = HTML_ESCAPES[i][2]; + assertEquals(message, expected, StringEscapeUtils.escapeHtml4(original)); + StringWriter sw = new StringWriter(); + try { + StringEscapeUtils.ESCAPE_HTML4.translate(original, sw); + } catch (IOException e) { + } + String actual = original == null ? null : sw.toString(); + assertEquals(message, expected, actual); + } + } + + @Test + public void testUnescapeHtml4() { + for (int i = 0; i < HTML_ESCAPES.length; ++i) { + String message = HTML_ESCAPES[i][0]; + String expected = HTML_ESCAPES[i][2]; + String original = HTML_ESCAPES[i][1]; + assertEquals(message, expected, StringEscapeUtils.unescapeHtml4(original)); + + StringWriter sw = new StringWriter(); + try { + StringEscapeUtils.UNESCAPE_HTML4.translate(original, sw); + } catch (IOException e) { + } + String actual = original == null ? null : sw.toString(); + assertEquals(message, expected, actual); + } + // \u00E7 is a cedilla (c with wiggle under) + // note that the test string must be 7-bit-clean (Unicode escaped) or else it will compile incorrectly + // on some locales + assertEquals("funny chars pass through OK", "Fran\u00E7ais", StringEscapeUtils.unescapeHtml4("Fran\u00E7ais")); + + assertEquals("Hello&;World", StringEscapeUtils.unescapeHtml4("Hello&;World")); + assertEquals("Hello&#;World", StringEscapeUtils.unescapeHtml4("Hello&#;World")); + assertEquals("Hello&# ;World", StringEscapeUtils.unescapeHtml4("Hello&# ;World")); + assertEquals("Hello&##;World", StringEscapeUtils.unescapeHtml4("Hello&##;World")); + } + + @Test + public void testUnescapeHexCharsHtml() { + // Simple easy to grok test + assertEquals("hex number unescape", "\u0080\u009F", StringEscapeUtils.unescapeHtml4("€Ÿ")); + assertEquals("hex number unescape", "\u0080\u009F", StringEscapeUtils.unescapeHtml4("€Ÿ")); + // Test all Character values: + for (char i = Character.MIN_VALUE; i < Character.MAX_VALUE; i++) { + Character c1 = new Character(i); + Character c2 = new Character((char)(i+1)); + String expected = c1.toString() + c2.toString(); + String escapedC1 = "&#x" + Integer.toHexString((c1.charValue())) + ";"; + String escapedC2 = "&#x" + Integer.toHexString((c2.charValue())) + ";"; + assertEquals("hex number unescape index " + (int)i, expected, StringEscapeUtils.unescapeHtml4(escapedC1 + escapedC2)); + } + } + + @Test + public void testUnescapeUnknownEntity() throws Exception { + assertEquals("&zzzz;", StringEscapeUtils.unescapeHtml4("&zzzz;")); + } + + @Test + public void testEscapeHtmlVersions() throws Exception { + assertEquals("Β", StringEscapeUtils.escapeHtml4("\u0392")); + assertEquals("\u0392", StringEscapeUtils.unescapeHtml4("Β")); + + // TODO: refine API for escaping/unescaping specific HTML versions + } + + @Test + public void testEscapeXml() throws Exception { + assertEquals("<abc>", StringEscapeUtils.escapeXml("")); + assertEquals("", StringEscapeUtils.unescapeXml("<abc>")); + + assertEquals("XML should not escape >0x7f values", + "\u00A1", StringEscapeUtils.escapeXml("\u00A1")); + assertEquals("XML should be able to unescape >0x7f values", + "\u00A0", StringEscapeUtils.unescapeXml(" ")); + assertEquals("XML should be able to unescape >0x7f values with one leading 0", + "\u00A0", StringEscapeUtils.unescapeXml(" ")); + assertEquals("XML should be able to unescape >0x7f values with two leading 0s", + "\u00A0", StringEscapeUtils.unescapeXml(" ")); + assertEquals("XML should be able to unescape >0x7f values with three leading 0s", + "\u00A0", StringEscapeUtils.unescapeXml(" ")); + + assertEquals("ain't", StringEscapeUtils.unescapeXml("ain't")); + assertEquals("ain't", StringEscapeUtils.escapeXml("ain't")); + assertEquals("", StringEscapeUtils.escapeXml("")); + assertEquals(null, StringEscapeUtils.escapeXml(null)); + assertEquals(null, StringEscapeUtils.unescapeXml(null)); + + StringWriter sw = new StringWriter(); + try { + StringEscapeUtils.ESCAPE_XML.translate("", sw); + } catch (IOException e) { + } + assertEquals("XML was escaped incorrectly", "<abc>", sw.toString() ); + + sw = new StringWriter(); + try { + StringEscapeUtils.UNESCAPE_XML.translate("<abc>", sw); + } catch (IOException e) { + } + assertEquals("XML was unescaped incorrectly", "", sw.toString() ); + } + + /** + * Tests Supplementary characters. + *

+ * From http://www.w3.org/International/questions/qa-escapes + *

+ *
+ * Supplementary characters are those Unicode characters that have code points higher than the characters in + * the Basic Multilingual Plane (BMP). In UTF-16 a supplementary character is encoded using two 16-bit surrogate code points from the + * BMP. Because of this, some people think that supplementary characters need to be represented using two escapes, but this is incorrect + * - you must use the single, code point value for that character. For example, use 𣎴 rather than ��. + *
+ * @see Using character escapes in markup and CSS + * @see LANG-728 + */ + @Test + public void testEscapeXmlSupplementaryCharacters() { + CharSequenceTranslator escapeXml = + StringEscapeUtils.ESCAPE_XML.with( NumericEntityEscaper.between(0x7f, Integer.MAX_VALUE) ); + + assertEquals("Supplementary character must be represented using a single escape", "𣎴", + escapeXml.translate("\uD84C\uDFB4")); + } + + /** + * Reverse of the above. + * + * @see LANG-729 + */ + @Test + public void testUnescapeXmlSupplementaryCharacters() { + assertEquals("Supplementary character must be represented using a single escape", "\uD84C\uDFB4", + StringEscapeUtils.unescapeXml("𣎴") ); + } + + // Tests issue #38569 + // http://issues.apache.org/bugzilla/show_bug.cgi?id=38569 + @Test + public void testStandaloneAmphersand() { + assertEquals("", StringEscapeUtils.unescapeHtml4("<P&O>")); + assertEquals("test & <", StringEscapeUtils.unescapeHtml4("test & <")); + assertEquals("", StringEscapeUtils.unescapeXml("<P&O>")); + assertEquals("test & <", StringEscapeUtils.unescapeXml("test & <")); + } + + @Test + public void testLang313() { + assertEquals("& &", StringEscapeUtils.unescapeHtml4("& &")); + } + + @Test + public void testEscapeCsvString() throws Exception { + assertEquals("foo.bar", StringEscapeUtils.escapeCsv("foo.bar")); + assertEquals("\"foo,bar\"", StringEscapeUtils.escapeCsv("foo,bar")); + assertEquals("\"foo\nbar\"", StringEscapeUtils.escapeCsv("foo\nbar")); + assertEquals("\"foo\rbar\"", StringEscapeUtils.escapeCsv("foo\rbar")); + assertEquals("\"foo\"\"bar\"", StringEscapeUtils.escapeCsv("foo\"bar")); + assertEquals("", StringEscapeUtils.escapeCsv("")); + assertEquals(null, StringEscapeUtils.escapeCsv(null)); + } + + @Test + public void testEscapeCsvWriter() throws Exception { + checkCsvEscapeWriter("foo.bar", "foo.bar"); + checkCsvEscapeWriter("\"foo,bar\"", "foo,bar"); + checkCsvEscapeWriter("\"foo\nbar\"", "foo\nbar"); + checkCsvEscapeWriter("\"foo\rbar\"", "foo\rbar"); + checkCsvEscapeWriter("\"foo\"\"bar\"", "foo\"bar"); + checkCsvEscapeWriter("", null); + checkCsvEscapeWriter("", ""); + } + + private void checkCsvEscapeWriter(String expected, String value) { + try { + StringWriter writer = new StringWriter(); + StringEscapeUtils.ESCAPE_CSV.translate(value, writer); + assertEquals(expected, writer.toString()); + } catch (IOException e) { + fail("Threw: " + e); + } + } + + @Test + public void testUnescapeCsvString() throws Exception { + assertEquals("foo.bar", StringEscapeUtils.unescapeCsv("foo.bar")); + assertEquals("foo,bar", StringEscapeUtils.unescapeCsv("\"foo,bar\"")); + assertEquals("foo\nbar", StringEscapeUtils.unescapeCsv("\"foo\nbar\"")); + assertEquals("foo\rbar", StringEscapeUtils.unescapeCsv("\"foo\rbar\"")); + assertEquals("foo\"bar", StringEscapeUtils.unescapeCsv("\"foo\"\"bar\"")); + assertEquals("", StringEscapeUtils.unescapeCsv("")); + assertEquals(null, StringEscapeUtils.unescapeCsv(null)); + + assertEquals("\"foo.bar\"", StringEscapeUtils.unescapeCsv("\"foo.bar\"")); + } + + @Test + public void testUnescapeCsvWriter() throws Exception { + checkCsvUnescapeWriter("foo.bar", "foo.bar"); + checkCsvUnescapeWriter("foo,bar", "\"foo,bar\""); + checkCsvUnescapeWriter("foo\nbar", "\"foo\nbar\""); + checkCsvUnescapeWriter("foo\rbar", "\"foo\rbar\""); + checkCsvUnescapeWriter("foo\"bar", "\"foo\"\"bar\""); + checkCsvUnescapeWriter("", null); + checkCsvUnescapeWriter("", ""); + + checkCsvUnescapeWriter("\"foo.bar\"", "\"foo.bar\""); + } + + private void checkCsvUnescapeWriter(String expected, String value) { + try { + StringWriter writer = new StringWriter(); + StringEscapeUtils.UNESCAPE_CSV.translate(value, writer); + assertEquals(expected, writer.toString()); + } catch (IOException e) { + fail("Threw: " + e); + } + } + + /** + * Tests // https://issues.apache.org/jira/browse/LANG-480 + * + * @throws java.io.UnsupportedEncodingException + */ + @Test + public void testEscapeHtmlHighUnicode() throws java.io.UnsupportedEncodingException { + // this is the utf8 representation of the character: + // COUNTING ROD UNIT DIGIT THREE + // in Unicode + // codepoint: U+1D362 + byte[] data = new byte[] { (byte)0xF0, (byte)0x9D, (byte)0x8D, (byte)0xA2 }; + + String original = new String(data, "UTF8"); + + String escaped = StringEscapeUtils.escapeHtml4( original ); + assertEquals( "High Unicode should not have been escaped", original, escaped); + + String unescaped = StringEscapeUtils.unescapeHtml4( escaped ); + assertEquals( "High Unicode should have been unchanged", original, unescaped); + +// TODO: I think this should hold, needs further investigation +// String unescapedFromEntity = StringEscapeUtils.unescapeHtml4( "𝍢" ); +// assertEquals( "High Unicode should have been unescaped", original, unescapedFromEntity); + } + + /** + * Tests https://issues.apache.org/jira/browse/LANG-339 + */ + @Test + public void testEscapeHiragana() { + // Some random Japanese Unicode characters + String original = "\u304B\u304C\u3068"; + String escaped = StringEscapeUtils.escapeHtml4(original); + assertEquals( "Hiragana character Unicode behaviour should not be being escaped by escapeHtml4", + original, escaped); + + String unescaped = StringEscapeUtils.unescapeHtml4( escaped ); + + assertEquals( "Hiragana character Unicode behaviour has changed - expected no unescaping", escaped, unescaped); + } + + /** + * Tests https://issues.apache.org/jira/browse/LANG-708 + * + * @throws IOException + * if an I/O error occurs + */ + @Test + public void testLang708() throws IOException { + String input = IOUtils.toString(new FileInputStream("src/test/resources/lang-708-input.txt"), "UTF-8"); + String escaped = StringEscapeUtils.escapeEcmaScript(input); + // just the end: + assertTrue(escaped, escaped.endsWith("}]")); + // a little more: + assertTrue(escaped, escaped.endsWith("\"valueCode\\\":\\\"\\\"}]")); + } + + /** + * Tests https://issues.apache.org/jira/browse/LANG-720 + */ + @Test + public void testLang720() { + String input = new StringBuilder("\ud842\udfb7").append("A").toString(); + String escaped = StringEscapeUtils.escapeXml(input); + assertEquals(input, escaped); + } +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/StringUtils.java b/ApacheCommonsLang/org/apache/commons/lang3/StringUtils.java new file mode 100644 index 0000000..0053835 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/StringUtils.java @@ -0,0 +1,6467 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3; + +import java.io.UnsupportedEncodingException; +import java.text.Normalizer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.regex.Pattern; + +/** + *

Operations on {@link java.lang.String} that are + * {@code null} safe.

+ * + *
    + *
  • IsEmpty/IsBlank + * - checks if a String contains text
  • + *
  • Trim/Strip + * - removes leading and trailing whitespace
  • + *
  • Equals + * - compares two strings null-safe
  • + *
  • startsWith + * - check if a String starts with a prefix null-safe
  • + *
  • endsWith + * - check if a String ends with a suffix null-safe
  • + *
  • IndexOf/LastIndexOf/Contains + * - null-safe index-of checks + *
  • IndexOfAny/LastIndexOfAny/IndexOfAnyBut/LastIndexOfAnyBut + * - index-of any of a set of Strings
  • + *
  • ContainsOnly/ContainsNone/ContainsAny + * - does String contains only/none/any of these characters
  • + *
  • Substring/Left/Right/Mid + * - null-safe substring extractions
  • + *
  • SubstringBefore/SubstringAfter/SubstringBetween + * - substring extraction relative to other strings
  • + *
  • Split/Join + * - splits a String into an array of substrings and vice versa
  • + *
  • Remove/Delete + * - removes part of a String
  • + *
  • Replace/Overlay + * - Searches a String and replaces one String with another
  • + *
  • Chomp/Chop + * - removes the last part of a String
  • + *
  • LeftPad/RightPad/Center/Repeat + * - pads a String
  • + *
  • UpperCase/LowerCase/SwapCase/Capitalize/Uncapitalize + * - changes the case of a String
  • + *
  • CountMatches + * - counts the number of occurrences of one String in another
  • + *
  • IsAlpha/IsNumeric/IsWhitespace/IsAsciiPrintable + * - checks the characters in a String
  • + *
  • DefaultString + * - protects against a null input String
  • + *
  • Reverse/ReverseDelimited + * - reverses a String
  • + *
  • Abbreviate + * - abbreviates a string using ellipsis
  • + *
  • Difference + * - compares Strings and reports on their differences
  • + *
  • LevenshteinDistance + * - the number of changes needed to change one String into another
  • + *
+ * + *

The {@code StringUtils} class defines certain words related to + * String handling.

+ * + *
    + *
  • null - {@code null}
  • + *
  • empty - a zero-length string ({@code ""})
  • + *
  • space - the space character ({@code ' '}, char 32)
  • + *
  • whitespace - the characters defined by {@link Character#isWhitespace(char)}
  • + *
  • trim - the characters <= 32 as in {@link String#trim()}
  • + *
+ * + *

{@code StringUtils} handles {@code null} input Strings quietly. + * That is to say that a {@code null} input will return {@code null}. + * Where a {@code boolean} or {@code int} is being returned + * details vary by method.

+ * + *

A side effect of the {@code null} handling is that a + * {@code NullPointerException} should be considered a bug in + * {@code StringUtils}.

+ * + *

Methods in this class give sample code to explain their operation. + * The symbol {@code *} is used to indicate any input including {@code null}.

+ * + *

#ThreadSafe#

+ * @see java.lang.String + * @since 1.0 + * @version $Id: StringUtils.java 1299413 2012-03-11 17:57:40Z ggregory $ + */ +//@Immutable +public class StringUtils { + // Performance testing notes (JDK 1.4, Jul03, scolebourne) + // Whitespace: + // Character.isWhitespace() is faster than WHITESPACE.indexOf() + // where WHITESPACE is a string of all whitespace characters + // + // Character access: + // String.charAt(n) versus toCharArray(), then array[n] + // String.charAt(n) is about 15% worse for a 10K string + // They are about equal for a length 50 string + // String.charAt(n) is about 4 times better for a length 3 string + // String.charAt(n) is best bet overall + // + // Append: + // String.concat about twice as fast as StringBuffer.append + // (not sure who tested this) + + /** + * The empty String {@code ""}. + * @since 2.0 + */ + public static final String EMPTY = ""; + + /** + * Represents a failed index search. + * @since 2.1 + */ + public static final int INDEX_NOT_FOUND = -1; + + /** + *

The maximum size to which the padding constant(s) can expand.

+ */ + private static final int PAD_LIMIT = 8192; + + /** + * A regex pattern for recognizing blocks of whitespace characters. + */ + private static final Pattern WHITESPACE_BLOCK = Pattern.compile("\\s+"); + + /** + *

{@code StringUtils} instances should NOT be constructed in + * standard programming. Instead, the class should be used as + * {@code StringUtils.trim(" foo ");}.

+ * + *

This constructor is public to permit tools that require a JavaBean + * instance to operate.

+ */ + public StringUtils() { + super(); + } + + // Empty checks + //----------------------------------------------------------------------- + /** + *

Checks if a CharSequence is empty ("") or null.

+ * + *
+     * StringUtils.isEmpty(null)      = true
+     * StringUtils.isEmpty("")        = true
+     * StringUtils.isEmpty(" ")       = false
+     * StringUtils.isEmpty("bob")     = false
+     * StringUtils.isEmpty("  bob  ") = false
+     * 
+ * + *

NOTE: This method changed in Lang version 2.0. + * It no longer trims the CharSequence. + * That functionality is available in isBlank().

+ * + * @param cs the CharSequence to check, may be null + * @return {@code true} if the CharSequence is empty or null + * @since 3.0 Changed signature from isEmpty(String) to isEmpty(CharSequence) + */ + public static boolean isEmpty(CharSequence cs) { + return cs == null || cs.length() == 0; + } + + /** + *

Checks if a CharSequence is not empty ("") and not null.

+ * + *
+     * StringUtils.isNotEmpty(null)      = false
+     * StringUtils.isNotEmpty("")        = false
+     * StringUtils.isNotEmpty(" ")       = true
+     * StringUtils.isNotEmpty("bob")     = true
+     * StringUtils.isNotEmpty("  bob  ") = true
+     * 
+ * + * @param cs the CharSequence to check, may be null + * @return {@code true} if the CharSequence is not empty and not null + * @since 3.0 Changed signature from isNotEmpty(String) to isNotEmpty(CharSequence) + */ + public static boolean isNotEmpty(CharSequence cs) { + return !StringUtils.isEmpty(cs); + } + + /** + *

Checks if a CharSequence is whitespace, empty ("") or null.

+ * + *
+     * StringUtils.isBlank(null)      = true
+     * StringUtils.isBlank("")        = true
+     * StringUtils.isBlank(" ")       = true
+     * StringUtils.isBlank("bob")     = false
+     * StringUtils.isBlank("  bob  ") = false
+     * 
+ * + * @param cs the CharSequence to check, may be null + * @return {@code true} if the CharSequence is null, empty or whitespace + * @since 2.0 + * @since 3.0 Changed signature from isBlank(String) to isBlank(CharSequence) + */ + public static boolean isBlank(CharSequence cs) { + int strLen; + if (cs == null || (strLen = cs.length()) == 0) { + return true; + } + for (int i = 0; i < strLen; i++) { + if (Character.isWhitespace(cs.charAt(i)) == false) { + return false; + } + } + return true; + } + + /** + *

Checks if a CharSequence is not empty (""), not null and not whitespace only.

+ * + *
+     * StringUtils.isNotBlank(null)      = false
+     * StringUtils.isNotBlank("")        = false
+     * StringUtils.isNotBlank(" ")       = false
+     * StringUtils.isNotBlank("bob")     = true
+     * StringUtils.isNotBlank("  bob  ") = true
+     * 
+ * + * @param cs the CharSequence to check, may be null + * @return {@code true} if the CharSequence is + * not empty and not null and not whitespace + * @since 2.0 + * @since 3.0 Changed signature from isNotBlank(String) to isNotBlank(CharSequence) + */ + public static boolean isNotBlank(CharSequence cs) { + return !StringUtils.isBlank(cs); + } + + // Trim + //----------------------------------------------------------------------- + /** + *

Removes control characters (char <= 32) from both + * ends of this String, handling {@code null} by returning + * {@code null}.

+ * + *

The String is trimmed using {@link String#trim()}. + * Trim removes start and end characters <= 32. + * To strip whitespace use {@link #strip(String)}.

+ * + *

To trim your choice of characters, use the + * {@link #strip(String, String)} methods.

+ * + *
+     * StringUtils.trim(null)          = null
+     * StringUtils.trim("")            = ""
+     * StringUtils.trim("     ")       = ""
+     * StringUtils.trim("abc")         = "abc"
+     * StringUtils.trim("    abc    ") = "abc"
+     * 
+ * + * @param str the String to be trimmed, may be null + * @return the trimmed string, {@code null} if null String input + */ + public static String trim(String str) { + return str == null ? null : str.trim(); + } + + /** + *

Removes control characters (char <= 32) from both + * ends of this String returning {@code null} if the String is + * empty ("") after the trim or if it is {@code null}. + * + *

The String is trimmed using {@link String#trim()}. + * Trim removes start and end characters <= 32. + * To strip whitespace use {@link #stripToNull(String)}.

+ * + *
+     * StringUtils.trimToNull(null)          = null
+     * StringUtils.trimToNull("")            = null
+     * StringUtils.trimToNull("     ")       = null
+     * StringUtils.trimToNull("abc")         = "abc"
+     * StringUtils.trimToNull("    abc    ") = "abc"
+     * 
+ * + * @param str the String to be trimmed, may be null + * @return the trimmed String, + * {@code null} if only chars <= 32, empty or null String input + * @since 2.0 + */ + public static String trimToNull(String str) { + String ts = trim(str); + return isEmpty(ts) ? null : ts; + } + + /** + *

Removes control characters (char <= 32) from both + * ends of this String returning an empty String ("") if the String + * is empty ("") after the trim or if it is {@code null}. + * + *

The String is trimmed using {@link String#trim()}. + * Trim removes start and end characters <= 32. + * To strip whitespace use {@link #stripToEmpty(String)}.

+ * + *
+     * StringUtils.trimToEmpty(null)          = ""
+     * StringUtils.trimToEmpty("")            = ""
+     * StringUtils.trimToEmpty("     ")       = ""
+     * StringUtils.trimToEmpty("abc")         = "abc"
+     * StringUtils.trimToEmpty("    abc    ") = "abc"
+     * 
+ * + * @param str the String to be trimmed, may be null + * @return the trimmed String, or an empty String if {@code null} input + * @since 2.0 + */ + public static String trimToEmpty(String str) { + return str == null ? EMPTY : str.trim(); + } + + // Stripping + //----------------------------------------------------------------------- + /** + *

Strips whitespace from the start and end of a String.

+ * + *

This is similar to {@link #trim(String)} but removes whitespace. + * Whitespace is defined by {@link Character#isWhitespace(char)}.

+ * + *

A {@code null} input String returns {@code null}.

+ * + *
+     * StringUtils.strip(null)     = null
+     * StringUtils.strip("")       = ""
+     * StringUtils.strip("   ")    = ""
+     * StringUtils.strip("abc")    = "abc"
+     * StringUtils.strip("  abc")  = "abc"
+     * StringUtils.strip("abc  ")  = "abc"
+     * StringUtils.strip(" abc ")  = "abc"
+     * StringUtils.strip(" ab c ") = "ab c"
+     * 
+ * + * @param str the String to remove whitespace from, may be null + * @return the stripped String, {@code null} if null String input + */ + public static String strip(String str) { + return strip(str, null); + } + + /** + *

Strips whitespace from the start and end of a String returning + * {@code null} if the String is empty ("") after the strip.

+ * + *

This is similar to {@link #trimToNull(String)} but removes whitespace. + * Whitespace is defined by {@link Character#isWhitespace(char)}.

+ * + *
+     * StringUtils.stripToNull(null)     = null
+     * StringUtils.stripToNull("")       = null
+     * StringUtils.stripToNull("   ")    = null
+     * StringUtils.stripToNull("abc")    = "abc"
+     * StringUtils.stripToNull("  abc")  = "abc"
+     * StringUtils.stripToNull("abc  ")  = "abc"
+     * StringUtils.stripToNull(" abc ")  = "abc"
+     * StringUtils.stripToNull(" ab c ") = "ab c"
+     * 
+ * + * @param str the String to be stripped, may be null + * @return the stripped String, + * {@code null} if whitespace, empty or null String input + * @since 2.0 + */ + public static String stripToNull(String str) { + if (str == null) { + return null; + } + str = strip(str, null); + return str.length() == 0 ? null : str; + } + + /** + *

Strips whitespace from the start and end of a String returning + * an empty String if {@code null} input.

+ * + *

This is similar to {@link #trimToEmpty(String)} but removes whitespace. + * Whitespace is defined by {@link Character#isWhitespace(char)}.

+ * + *
+     * StringUtils.stripToEmpty(null)     = ""
+     * StringUtils.stripToEmpty("")       = ""
+     * StringUtils.stripToEmpty("   ")    = ""
+     * StringUtils.stripToEmpty("abc")    = "abc"
+     * StringUtils.stripToEmpty("  abc")  = "abc"
+     * StringUtils.stripToEmpty("abc  ")  = "abc"
+     * StringUtils.stripToEmpty(" abc ")  = "abc"
+     * StringUtils.stripToEmpty(" ab c ") = "ab c"
+     * 
+ * + * @param str the String to be stripped, may be null + * @return the trimmed String, or an empty String if {@code null} input + * @since 2.0 + */ + public static String stripToEmpty(String str) { + return str == null ? EMPTY : strip(str, null); + } + + /** + *

Strips any of a set of characters from the start and end of a String. + * This is similar to {@link String#trim()} but allows the characters + * to be stripped to be controlled.

+ * + *

A {@code null} input String returns {@code null}. + * An empty string ("") input returns the empty string.

+ * + *

If the stripChars String is {@code null}, whitespace is + * stripped as defined by {@link Character#isWhitespace(char)}. + * Alternatively use {@link #strip(String)}.

+ * + *
+     * StringUtils.strip(null, *)          = null
+     * StringUtils.strip("", *)            = ""
+     * StringUtils.strip("abc", null)      = "abc"
+     * StringUtils.strip("  abc", null)    = "abc"
+     * StringUtils.strip("abc  ", null)    = "abc"
+     * StringUtils.strip(" abc ", null)    = "abc"
+     * StringUtils.strip("  abcyx", "xyz") = "  abc"
+     * 
+ * + * @param str the String to remove characters from, may be null + * @param stripChars the characters to remove, null treated as whitespace + * @return the stripped String, {@code null} if null String input + */ + public static String strip(String str, String stripChars) { + if (isEmpty(str)) { + return str; + } + str = stripStart(str, stripChars); + return stripEnd(str, stripChars); + } + + /** + *

Strips any of a set of characters from the start of a String.

+ * + *

A {@code null} input String returns {@code null}. + * An empty string ("") input returns the empty string.

+ * + *

If the stripChars String is {@code null}, whitespace is + * stripped as defined by {@link Character#isWhitespace(char)}.

+ * + *
+     * StringUtils.stripStart(null, *)          = null
+     * StringUtils.stripStart("", *)            = ""
+     * StringUtils.stripStart("abc", "")        = "abc"
+     * StringUtils.stripStart("abc", null)      = "abc"
+     * StringUtils.stripStart("  abc", null)    = "abc"
+     * StringUtils.stripStart("abc  ", null)    = "abc  "
+     * StringUtils.stripStart(" abc ", null)    = "abc "
+     * StringUtils.stripStart("yxabc  ", "xyz") = "abc  "
+     * 
+ * + * @param str the String to remove characters from, may be null + * @param stripChars the characters to remove, null treated as whitespace + * @return the stripped String, {@code null} if null String input + */ + public static String stripStart(String str, String stripChars) { + int strLen; + if (str == null || (strLen = str.length()) == 0) { + return str; + } + int start = 0; + if (stripChars == null) { + while (start != strLen && Character.isWhitespace(str.charAt(start))) { + start++; + } + } else if (stripChars.length() == 0) { + return str; + } else { + while (start != strLen && stripChars.indexOf(str.charAt(start)) != INDEX_NOT_FOUND) { + start++; + } + } + return str.substring(start); + } + + /** + *

Strips any of a set of characters from the end of a String.

+ * + *

A {@code null} input String returns {@code null}. + * An empty string ("") input returns the empty string.

+ * + *

If the stripChars String is {@code null}, whitespace is + * stripped as defined by {@link Character#isWhitespace(char)}.

+ * + *
+     * StringUtils.stripEnd(null, *)          = null
+     * StringUtils.stripEnd("", *)            = ""
+     * StringUtils.stripEnd("abc", "")        = "abc"
+     * StringUtils.stripEnd("abc", null)      = "abc"
+     * StringUtils.stripEnd("  abc", null)    = "  abc"
+     * StringUtils.stripEnd("abc  ", null)    = "abc"
+     * StringUtils.stripEnd(" abc ", null)    = " abc"
+     * StringUtils.stripEnd("  abcyx", "xyz") = "  abc"
+     * StringUtils.stripEnd("120.00", ".0")   = "12"
+     * 
+ * + * @param str the String to remove characters from, may be null + * @param stripChars the set of characters to remove, null treated as whitespace + * @return the stripped String, {@code null} if null String input + */ + public static String stripEnd(String str, String stripChars) { + int end; + if (str == null || (end = str.length()) == 0) { + return str; + } + + if (stripChars == null) { + while (end != 0 && Character.isWhitespace(str.charAt(end - 1))) { + end--; + } + } else if (stripChars.length() == 0) { + return str; + } else { + while (end != 0 && stripChars.indexOf(str.charAt(end - 1)) != INDEX_NOT_FOUND) { + end--; + } + } + return str.substring(0, end); + } + + // StripAll + //----------------------------------------------------------------------- + /** + *

Strips whitespace from the start and end of every String in an array. + * Whitespace is defined by {@link Character#isWhitespace(char)}.

+ * + *

A new array is returned each time, except for length zero. + * A {@code null} array will return {@code null}. + * An empty array will return itself. + * A {@code null} array entry will be ignored.

+ * + *
+     * StringUtils.stripAll(null)             = null
+     * StringUtils.stripAll([])               = []
+     * StringUtils.stripAll(["abc", "  abc"]) = ["abc", "abc"]
+     * StringUtils.stripAll(["abc  ", null])  = ["abc", null]
+     * 
+ * + * @param strs the array to remove whitespace from, may be null + * @return the stripped Strings, {@code null} if null array input + */ + public static String[] stripAll(String... strs) { + return stripAll(strs, null); + } + + /** + *

Strips any of a set of characters from the start and end of every + * String in an array.

+ * Whitespace is defined by {@link Character#isWhitespace(char)}.

+ * + *

A new array is returned each time, except for length zero. + * A {@code null} array will return {@code null}. + * An empty array will return itself. + * A {@code null} array entry will be ignored. + * A {@code null} stripChars will strip whitespace as defined by + * {@link Character#isWhitespace(char)}.

+ * + *
+     * StringUtils.stripAll(null, *)                = null
+     * StringUtils.stripAll([], *)                  = []
+     * StringUtils.stripAll(["abc", "  abc"], null) = ["abc", "abc"]
+     * StringUtils.stripAll(["abc  ", null], null)  = ["abc", null]
+     * StringUtils.stripAll(["abc  ", null], "yz")  = ["abc  ", null]
+     * StringUtils.stripAll(["yabcz", null], "yz")  = ["abc", null]
+     * 
+ * + * @param strs the array to remove characters from, may be null + * @param stripChars the characters to remove, null treated as whitespace + * @return the stripped Strings, {@code null} if null array input + */ + public static String[] stripAll(String[] strs, String stripChars) { + int strsLen; + if (strs == null || (strsLen = strs.length) == 0) { + return strs; + } + String[] newArr = new String[strsLen]; + for (int i = 0; i < strsLen; i++) { + newArr[i] = strip(strs[i], stripChars); + } + return newArr; + } + + /** + *

Removes diacritics (~= accents) from a string. The case will not be altered.

+ *

For instance, 'à' will be replaced by 'a'.

+ *

Note that ligatures will be left as is.

+ * + *
+     * StringUtils.stripAccents(null)                = null
+     * StringUtils.stripAccents("")                  = ""
+     * StringUtils.stripAccents("control")           = "control"
+     * StringUtils.stripAccents("éclair")     = "eclair"
+     * 
+ * + * @param input String to be stripped + * @return input text with diacritics removed + * + * @since 3.0 + */ + // See also Lucene's ASCIIFoldingFilter (Lucene 2.9) that replaces accented characters by their unaccented equivalent (and uncommitted bug fix: https://issues.apache.org/jira/browse/LUCENE-1343?focusedCommentId=12858907&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#action_12858907). + public static String stripAccents(String input) { + if(input == null) { + return null; + } + Pattern pattern = Pattern.compile("\\p{InCombiningDiacriticalMarks}+");//$NON-NLS-1$ + String decomposed = Normalizer.normalize(input, Normalizer.Form.NFD); + // Note that this doesn't correctly remove ligatures... + return pattern.matcher(decomposed).replaceAll("");//$NON-NLS-1$ + } + + // Equals + //----------------------------------------------------------------------- + /** + *

Compares two CharSequences, returning {@code true} if they represent + * equal sequences of characters.

+ * + *

{@code null}s are handled without exceptions. Two {@code null} + * references are considered to be equal. The comparison is case sensitive.

+ * + *
+     * StringUtils.equals(null, null)   = true
+     * StringUtils.equals(null, "abc")  = false
+     * StringUtils.equals("abc", null)  = false
+     * StringUtils.equals("abc", "abc") = true
+     * StringUtils.equals("abc", "ABC") = false
+     * 
+ * + * @see Object#equals(Object) + * @param cs1 the first CharSequence, may be {@code null} + * @param cs2 the second CharSequence, may be {@code null} + * @return {@code true} if the CharSequences are equal (case-sensitive), or both {@code null} + * @since 3.0 Changed signature from equals(String, String) to equals(CharSequence, CharSequence) + */ + public static boolean equals(CharSequence cs1, CharSequence cs2) { + if (cs1 == cs2) { + return true; + } + if (cs1 == null || cs2 == null) { + return false; + } + if (cs1 instanceof String && cs2 instanceof String) { + return cs1.equals(cs2); + } + return CharSequenceUtils.regionMatches(cs1, false, 0, cs2, 0, Math.max(cs1.length(), cs2.length())); + } + + /** + *

Compares two CharSequences, returning {@code true} if they represent + * equal sequences of characters, ignoring case.

+ * + *

{@code null}s are handled without exceptions. Two {@code null} + * references are considered equal. Comparison is case insensitive.

+ * + *
+     * StringUtils.equalsIgnoreCase(null, null)   = true
+     * StringUtils.equalsIgnoreCase(null, "abc")  = false
+     * StringUtils.equalsIgnoreCase("abc", null)  = false
+     * StringUtils.equalsIgnoreCase("abc", "abc") = true
+     * StringUtils.equalsIgnoreCase("abc", "ABC") = true
+     * 
+ * + * @param str1 the first CharSequence, may be null + * @param str2 the second CharSequence, may be null + * @return {@code true} if the CharSequence are equal, case insensitive, or + * both {@code null} + * @since 3.0 Changed signature from equalsIgnoreCase(String, String) to equalsIgnoreCase(CharSequence, CharSequence) + */ + public static boolean equalsIgnoreCase(CharSequence str1, CharSequence str2) { + if (str1 == null || str2 == null) { + return str1 == str2; + } else { + return CharSequenceUtils.regionMatches(str1, true, 0, str2, 0, Math.max(str1.length(), str2.length())); + } + } + + // IndexOf + //----------------------------------------------------------------------- + /** + *

Finds the first index within a CharSequence, handling {@code null}. + * This method uses {@link String#indexOf(int, int)} if possible.

+ * + *

A {@code null} or empty ("") CharSequence will return {@code INDEX_NOT_FOUND (-1)}.

+ * + *
+     * StringUtils.indexOf(null, *)         = -1
+     * StringUtils.indexOf("", *)           = -1
+     * StringUtils.indexOf("aabaabaa", 'a') = 0
+     * StringUtils.indexOf("aabaabaa", 'b') = 2
+     * 
+ * + * @param seq the CharSequence to check, may be null + * @param searchChar the character to find + * @return the first index of the search character, + * -1 if no match or {@code null} string input + * @since 2.0 + * @since 3.0 Changed signature from indexOf(String, int) to indexOf(CharSequence, int) + */ + public static int indexOf(CharSequence seq, int searchChar) { + if (isEmpty(seq)) { + return INDEX_NOT_FOUND; + } + return CharSequenceUtils.indexOf(seq, searchChar, 0); + } + + /** + *

Finds the first index within a CharSequence from a start position, + * handling {@code null}. + * This method uses {@link String#indexOf(int, int)} if possible.

+ * + *

A {@code null} or empty ("") CharSequence will return {@code (INDEX_NOT_FOUND) -1}. + * A negative start position is treated as zero. + * A start position greater than the string length returns {@code -1}.

+ * + *
+     * StringUtils.indexOf(null, *, *)          = -1
+     * StringUtils.indexOf("", *, *)            = -1
+     * StringUtils.indexOf("aabaabaa", 'b', 0)  = 2
+     * StringUtils.indexOf("aabaabaa", 'b', 3)  = 5
+     * StringUtils.indexOf("aabaabaa", 'b', 9)  = -1
+     * StringUtils.indexOf("aabaabaa", 'b', -1) = 2
+     * 
+ * + * @param seq the CharSequence to check, may be null + * @param searchChar the character to find + * @param startPos the start position, negative treated as zero + * @return the first index of the search character, + * -1 if no match or {@code null} string input + * @since 2.0 + * @since 3.0 Changed signature from indexOf(String, int, int) to indexOf(CharSequence, int, int) + */ + public static int indexOf(CharSequence seq, int searchChar, int startPos) { + if (isEmpty(seq)) { + return INDEX_NOT_FOUND; + } + return CharSequenceUtils.indexOf(seq, searchChar, startPos); + } + + /** + *

Finds the first index within a CharSequence, handling {@code null}. + * This method uses {@link String#indexOf(String, int)} if possible.

+ * + *

A {@code null} CharSequence will return {@code -1}.

+ * + *
+     * StringUtils.indexOf(null, *)          = -1
+     * StringUtils.indexOf(*, null)          = -1
+     * StringUtils.indexOf("", "")           = 0
+     * StringUtils.indexOf("", *)            = -1 (except when * = "")
+     * StringUtils.indexOf("aabaabaa", "a")  = 0
+     * StringUtils.indexOf("aabaabaa", "b")  = 2
+     * StringUtils.indexOf("aabaabaa", "ab") = 1
+     * StringUtils.indexOf("aabaabaa", "")   = 0
+     * 
+ * + * @param seq the CharSequence to check, may be null + * @param searchSeq the CharSequence to find, may be null + * @return the first index of the search CharSequence, + * -1 if no match or {@code null} string input + * @since 2.0 + * @since 3.0 Changed signature from indexOf(String, String) to indexOf(CharSequence, CharSequence) + */ + public static int indexOf(CharSequence seq, CharSequence searchSeq) { + if (seq == null || searchSeq == null) { + return INDEX_NOT_FOUND; + } + return CharSequenceUtils.indexOf(seq, searchSeq, 0); + } + + /** + *

Finds the first index within a CharSequence, handling {@code null}. + * This method uses {@link String#indexOf(String, int)} if possible.

+ * + *

A {@code null} CharSequence will return {@code -1}. + * A negative start position is treated as zero. + * An empty ("") search CharSequence always matches. + * A start position greater than the string length only matches + * an empty search CharSequence.

+ * + *
+     * StringUtils.indexOf(null, *, *)          = -1
+     * StringUtils.indexOf(*, null, *)          = -1
+     * StringUtils.indexOf("", "", 0)           = 0
+     * StringUtils.indexOf("", *, 0)            = -1 (except when * = "")
+     * StringUtils.indexOf("aabaabaa", "a", 0)  = 0
+     * StringUtils.indexOf("aabaabaa", "b", 0)  = 2
+     * StringUtils.indexOf("aabaabaa", "ab", 0) = 1
+     * StringUtils.indexOf("aabaabaa", "b", 3)  = 5
+     * StringUtils.indexOf("aabaabaa", "b", 9)  = -1
+     * StringUtils.indexOf("aabaabaa", "b", -1) = 2
+     * StringUtils.indexOf("aabaabaa", "", 2)   = 2
+     * StringUtils.indexOf("abc", "", 9)        = 3
+     * 
+ * + * @param seq the CharSequence to check, may be null + * @param searchSeq the CharSequence to find, may be null + * @param startPos the start position, negative treated as zero + * @return the first index of the search CharSequence, + * -1 if no match or {@code null} string input + * @since 2.0 + * @since 3.0 Changed signature from indexOf(String, String, int) to indexOf(CharSequence, CharSequence, int) + */ + public static int indexOf(CharSequence seq, CharSequence searchSeq, int startPos) { + if (seq == null || searchSeq == null) { + return INDEX_NOT_FOUND; + } + return CharSequenceUtils.indexOf(seq, searchSeq, startPos); + } + + /** + *

Finds the n-th index within a CharSequence, handling {@code null}. + * This method uses {@link String#indexOf(String)} if possible.

+ * + *

A {@code null} CharSequence will return {@code -1}.

+ * + *
+     * StringUtils.ordinalIndexOf(null, *, *)          = -1
+     * StringUtils.ordinalIndexOf(*, null, *)          = -1
+     * StringUtils.ordinalIndexOf("", "", *)           = 0
+     * StringUtils.ordinalIndexOf("aabaabaa", "a", 1)  = 0
+     * StringUtils.ordinalIndexOf("aabaabaa", "a", 2)  = 1
+     * StringUtils.ordinalIndexOf("aabaabaa", "b", 1)  = 2
+     * StringUtils.ordinalIndexOf("aabaabaa", "b", 2)  = 5
+     * StringUtils.ordinalIndexOf("aabaabaa", "ab", 1) = 1
+     * StringUtils.ordinalIndexOf("aabaabaa", "ab", 2) = 4
+     * StringUtils.ordinalIndexOf("aabaabaa", "", 1)   = 0
+     * StringUtils.ordinalIndexOf("aabaabaa", "", 2)   = 0
+     * 
+ * + *

Note that 'head(CharSequence str, int n)' may be implemented as:

+ * + *
+     *   str.substring(0, lastOrdinalIndexOf(str, "\n", n))
+     * 
+ * + * @param str the CharSequence to check, may be null + * @param searchStr the CharSequence to find, may be null + * @param ordinal the n-th {@code searchStr} to find + * @return the n-th index of the search CharSequence, + * {@code -1} ({@code INDEX_NOT_FOUND}) if no match or {@code null} string input + * @since 2.1 + * @since 3.0 Changed signature from ordinalIndexOf(String, String, int) to ordinalIndexOf(CharSequence, CharSequence, int) + */ + public static int ordinalIndexOf(CharSequence str, CharSequence searchStr, int ordinal) { + return ordinalIndexOf(str, searchStr, ordinal, false); + } + + /** + *

Finds the n-th index within a String, handling {@code null}. + * This method uses {@link String#indexOf(String)} if possible.

+ * + *

A {@code null} CharSequence will return {@code -1}.

+ * + * @param str the CharSequence to check, may be null + * @param searchStr the CharSequence to find, may be null + * @param ordinal the n-th {@code searchStr} to find + * @param lastIndex true if lastOrdinalIndexOf() otherwise false if ordinalIndexOf() + * @return the n-th index of the search CharSequence, + * {@code -1} ({@code INDEX_NOT_FOUND}) if no match or {@code null} string input + */ + // Shared code between ordinalIndexOf(String,String,int) and lastOrdinalIndexOf(String,String,int) + private static int ordinalIndexOf(CharSequence str, CharSequence searchStr, int ordinal, boolean lastIndex) { + if (str == null || searchStr == null || ordinal <= 0) { + return INDEX_NOT_FOUND; + } + if (searchStr.length() == 0) { + return lastIndex ? str.length() : 0; + } + int found = 0; + int index = lastIndex ? str.length() : INDEX_NOT_FOUND; + do { + if (lastIndex) { + index = CharSequenceUtils.lastIndexOf(str, searchStr, index - 1); + } else { + index = CharSequenceUtils.indexOf(str, searchStr, index + 1); + } + if (index < 0) { + return index; + } + found++; + } while (found < ordinal); + return index; + } + + /** + *

Case in-sensitive find of the first index within a CharSequence.

+ * + *

A {@code null} CharSequence will return {@code -1}. + * A negative start position is treated as zero. + * An empty ("") search CharSequence always matches. + * A start position greater than the string length only matches + * an empty search CharSequence.

+ * + *
+     * StringUtils.indexOfIgnoreCase(null, *)          = -1
+     * StringUtils.indexOfIgnoreCase(*, null)          = -1
+     * StringUtils.indexOfIgnoreCase("", "")           = 0
+     * StringUtils.indexOfIgnoreCase("aabaabaa", "a")  = 0
+     * StringUtils.indexOfIgnoreCase("aabaabaa", "b")  = 2
+     * StringUtils.indexOfIgnoreCase("aabaabaa", "ab") = 1
+     * 
+ * + * @param str the CharSequence to check, may be null + * @param searchStr the CharSequence to find, may be null + * @return the first index of the search CharSequence, + * -1 if no match or {@code null} string input + * @since 2.5 + * @since 3.0 Changed signature from indexOfIgnoreCase(String, String) to indexOfIgnoreCase(CharSequence, CharSequence) + */ + public static int indexOfIgnoreCase(CharSequence str, CharSequence searchStr) { + return indexOfIgnoreCase(str, searchStr, 0); + } + + /** + *

Case in-sensitive find of the first index within a CharSequence + * from the specified position.

+ * + *

A {@code null} CharSequence will return {@code -1}. + * A negative start position is treated as zero. + * An empty ("") search CharSequence always matches. + * A start position greater than the string length only matches + * an empty search CharSequence.

+ * + *
+     * StringUtils.indexOfIgnoreCase(null, *, *)          = -1
+     * StringUtils.indexOfIgnoreCase(*, null, *)          = -1
+     * StringUtils.indexOfIgnoreCase("", "", 0)           = 0
+     * StringUtils.indexOfIgnoreCase("aabaabaa", "A", 0)  = 0
+     * StringUtils.indexOfIgnoreCase("aabaabaa", "B", 0)  = 2
+     * StringUtils.indexOfIgnoreCase("aabaabaa", "AB", 0) = 1
+     * StringUtils.indexOfIgnoreCase("aabaabaa", "B", 3)  = 5
+     * StringUtils.indexOfIgnoreCase("aabaabaa", "B", 9)  = -1
+     * StringUtils.indexOfIgnoreCase("aabaabaa", "B", -1) = 2
+     * StringUtils.indexOfIgnoreCase("aabaabaa", "", 2)   = 2
+     * StringUtils.indexOfIgnoreCase("abc", "", 9)        = 3
+     * 
+ * + * @param str the CharSequence to check, may be null + * @param searchStr the CharSequence to find, may be null + * @param startPos the start position, negative treated as zero + * @return the first index of the search CharSequence, + * -1 if no match or {@code null} string input + * @since 2.5 + * @since 3.0 Changed signature from indexOfIgnoreCase(String, String, int) to indexOfIgnoreCase(CharSequence, CharSequence, int) + */ + public static int indexOfIgnoreCase(CharSequence str, CharSequence searchStr, int startPos) { + if (str == null || searchStr == null) { + return INDEX_NOT_FOUND; + } + if (startPos < 0) { + startPos = 0; + } + int endLimit = str.length() - searchStr.length() + 1; + if (startPos > endLimit) { + return INDEX_NOT_FOUND; + } + if (searchStr.length() == 0) { + return startPos; + } + for (int i = startPos; i < endLimit; i++) { + if (CharSequenceUtils.regionMatches(str, true, i, searchStr, 0, searchStr.length())) { + return i; + } + } + return INDEX_NOT_FOUND; + } + + // LastIndexOf + //----------------------------------------------------------------------- + /** + *

Finds the last index within a CharSequence, handling {@code null}. + * This method uses {@link String#lastIndexOf(int)} if possible.

+ * + *

A {@code null} or empty ("") CharSequence will return {@code -1}.

+ * + *
+     * StringUtils.lastIndexOf(null, *)         = -1
+     * StringUtils.lastIndexOf("", *)           = -1
+     * StringUtils.lastIndexOf("aabaabaa", 'a') = 7
+     * StringUtils.lastIndexOf("aabaabaa", 'b') = 5
+     * 
+ * + * @param seq the CharSequence to check, may be null + * @param searchChar the character to find + * @return the last index of the search character, + * -1 if no match or {@code null} string input + * @since 2.0 + * @since 3.0 Changed signature from lastIndexOf(String, int) to lastIndexOf(CharSequence, int) + */ + public static int lastIndexOf(CharSequence seq, int searchChar) { + if (isEmpty(seq)) { + return INDEX_NOT_FOUND; + } + return CharSequenceUtils.lastIndexOf(seq, searchChar, seq.length()); + } + + /** + *

Finds the last index within a CharSequence from a start position, + * handling {@code null}. + * This method uses {@link String#lastIndexOf(int, int)} if possible.

+ * + *

A {@code null} or empty ("") CharSequence will return {@code -1}. + * A negative start position returns {@code -1}. + * A start position greater than the string length searches the whole string.

+ * + *
+     * StringUtils.lastIndexOf(null, *, *)          = -1
+     * StringUtils.lastIndexOf("", *,  *)           = -1
+     * StringUtils.lastIndexOf("aabaabaa", 'b', 8)  = 5
+     * StringUtils.lastIndexOf("aabaabaa", 'b', 4)  = 2
+     * StringUtils.lastIndexOf("aabaabaa", 'b', 0)  = -1
+     * StringUtils.lastIndexOf("aabaabaa", 'b', 9)  = 5
+     * StringUtils.lastIndexOf("aabaabaa", 'b', -1) = -1
+     * StringUtils.lastIndexOf("aabaabaa", 'a', 0)  = 0
+     * 
+ * + * @param seq the CharSequence to check, may be null + * @param searchChar the character to find + * @param startPos the start position + * @return the last index of the search character, + * -1 if no match or {@code null} string input + * @since 2.0 + * @since 3.0 Changed signature from lastIndexOf(String, int, int) to lastIndexOf(CharSequence, int, int) + */ + public static int lastIndexOf(CharSequence seq, int searchChar, int startPos) { + if (isEmpty(seq)) { + return INDEX_NOT_FOUND; + } + return CharSequenceUtils.lastIndexOf(seq, searchChar, startPos); + } + + /** + *

Finds the last index within a CharSequence, handling {@code null}. + * This method uses {@link String#lastIndexOf(String)} if possible.

+ * + *

A {@code null} CharSequence will return {@code -1}.

+ * + *
+     * StringUtils.lastIndexOf(null, *)          = -1
+     * StringUtils.lastIndexOf(*, null)          = -1
+     * StringUtils.lastIndexOf("", "")           = 0
+     * StringUtils.lastIndexOf("aabaabaa", "a")  = 7
+     * StringUtils.lastIndexOf("aabaabaa", "b")  = 5
+     * StringUtils.lastIndexOf("aabaabaa", "ab") = 4
+     * StringUtils.lastIndexOf("aabaabaa", "")   = 8
+     * 
+ * + * @param seq the CharSequence to check, may be null + * @param searchSeq the CharSequence to find, may be null + * @return the last index of the search String, + * -1 if no match or {@code null} string input + * @since 2.0 + * @since 3.0 Changed signature from lastIndexOf(String, String) to lastIndexOf(CharSequence, CharSequence) + */ + public static int lastIndexOf(CharSequence seq, CharSequence searchSeq) { + if (seq == null || searchSeq == null) { + return INDEX_NOT_FOUND; + } + return CharSequenceUtils.lastIndexOf(seq, searchSeq, seq.length()); + } + + /** + *

Finds the n-th last index within a String, handling {@code null}. + * This method uses {@link String#lastIndexOf(String)}.

+ * + *

A {@code null} String will return {@code -1}.

+ * + *
+     * StringUtils.lastOrdinalIndexOf(null, *, *)          = -1
+     * StringUtils.lastOrdinalIndexOf(*, null, *)          = -1
+     * StringUtils.lastOrdinalIndexOf("", "", *)           = 0
+     * StringUtils.lastOrdinalIndexOf("aabaabaa", "a", 1)  = 7
+     * StringUtils.lastOrdinalIndexOf("aabaabaa", "a", 2)  = 6
+     * StringUtils.lastOrdinalIndexOf("aabaabaa", "b", 1)  = 5
+     * StringUtils.lastOrdinalIndexOf("aabaabaa", "b", 2)  = 2
+     * StringUtils.lastOrdinalIndexOf("aabaabaa", "ab", 1) = 4
+     * StringUtils.lastOrdinalIndexOf("aabaabaa", "ab", 2) = 1
+     * StringUtils.lastOrdinalIndexOf("aabaabaa", "", 1)   = 8
+     * StringUtils.lastOrdinalIndexOf("aabaabaa", "", 2)   = 8
+     * 
+ * + *

Note that 'tail(CharSequence str, int n)' may be implemented as:

+ * + *
+     *   str.substring(lastOrdinalIndexOf(str, "\n", n) + 1)
+     * 
+ * + * @param str the CharSequence to check, may be null + * @param searchStr the CharSequence to find, may be null + * @param ordinal the n-th last {@code searchStr} to find + * @return the n-th last index of the search CharSequence, + * {@code -1} ({@code INDEX_NOT_FOUND}) if no match or {@code null} string input + * @since 2.5 + * @since 3.0 Changed signature from lastOrdinalIndexOf(String, String, int) to lastOrdinalIndexOf(CharSequence, CharSequence, int) + */ + public static int lastOrdinalIndexOf(CharSequence str, CharSequence searchStr, int ordinal) { + return ordinalIndexOf(str, searchStr, ordinal, true); + } + + /** + *

Finds the first index within a CharSequence, handling {@code null}. + * This method uses {@link String#lastIndexOf(String, int)} if possible.

+ * + *

A {@code null} CharSequence will return {@code -1}. + * A negative start position returns {@code -1}. + * An empty ("") search CharSequence always matches unless the start position is negative. + * A start position greater than the string length searches the whole string.

+ * + *
+     * StringUtils.lastIndexOf(null, *, *)          = -1
+     * StringUtils.lastIndexOf(*, null, *)          = -1
+     * StringUtils.lastIndexOf("aabaabaa", "a", 8)  = 7
+     * StringUtils.lastIndexOf("aabaabaa", "b", 8)  = 5
+     * StringUtils.lastIndexOf("aabaabaa", "ab", 8) = 4
+     * StringUtils.lastIndexOf("aabaabaa", "b", 9)  = 5
+     * StringUtils.lastIndexOf("aabaabaa", "b", -1) = -1
+     * StringUtils.lastIndexOf("aabaabaa", "a", 0)  = 0
+     * StringUtils.lastIndexOf("aabaabaa", "b", 0)  = -1
+     * 
+ * + * @param seq the CharSequence to check, may be null + * @param searchSeq the CharSequence to find, may be null + * @param startPos the start position, negative treated as zero + * @return the first index of the search CharSequence, + * -1 if no match or {@code null} string input + * @since 2.0 + * @since 3.0 Changed signature from lastIndexOf(String, String, int) to lastIndexOf(CharSequence, CharSequence, int) + */ + public static int lastIndexOf(CharSequence seq, CharSequence searchSeq, int startPos) { + if (seq == null || searchSeq == null) { + return INDEX_NOT_FOUND; + } + return CharSequenceUtils.lastIndexOf(seq, searchSeq, startPos); + } + + /** + *

Case in-sensitive find of the last index within a CharSequence.

+ * + *

A {@code null} CharSequence will return {@code -1}. + * A negative start position returns {@code -1}. + * An empty ("") search CharSequence always matches unless the start position is negative. + * A start position greater than the string length searches the whole string.

+ * + *
+     * StringUtils.lastIndexOfIgnoreCase(null, *)          = -1
+     * StringUtils.lastIndexOfIgnoreCase(*, null)          = -1
+     * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "A")  = 7
+     * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "B")  = 5
+     * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "AB") = 4
+     * 
+ * + * @param str the CharSequence to check, may be null + * @param searchStr the CharSequence to find, may be null + * @return the first index of the search CharSequence, + * -1 if no match or {@code null} string input + * @since 2.5 + * @since 3.0 Changed signature from lastIndexOfIgnoreCase(String, String) to lastIndexOfIgnoreCase(CharSequence, CharSequence) + */ + public static int lastIndexOfIgnoreCase(CharSequence str, CharSequence searchStr) { + if (str == null || searchStr == null) { + return INDEX_NOT_FOUND; + } + return lastIndexOfIgnoreCase(str, searchStr, str.length()); + } + + /** + *

Case in-sensitive find of the last index within a CharSequence + * from the specified position.

+ * + *

A {@code null} CharSequence will return {@code -1}. + * A negative start position returns {@code -1}. + * An empty ("") search CharSequence always matches unless the start position is negative. + * A start position greater than the string length searches the whole string.

+ * + *
+     * StringUtils.lastIndexOfIgnoreCase(null, *, *)          = -1
+     * StringUtils.lastIndexOfIgnoreCase(*, null, *)          = -1
+     * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "A", 8)  = 7
+     * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "B", 8)  = 5
+     * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "AB", 8) = 4
+     * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "B", 9)  = 5
+     * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "B", -1) = -1
+     * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "A", 0)  = 0
+     * StringUtils.lastIndexOfIgnoreCase("aabaabaa", "B", 0)  = -1
+     * 
+ * + * @param str the CharSequence to check, may be null + * @param searchStr the CharSequence to find, may be null + * @param startPos the start position + * @return the first index of the search CharSequence, + * -1 if no match or {@code null} input + * @since 2.5 + * @since 3.0 Changed signature from lastIndexOfIgnoreCase(String, String, int) to lastIndexOfIgnoreCase(CharSequence, CharSequence, int) + */ + public static int lastIndexOfIgnoreCase(CharSequence str, CharSequence searchStr, int startPos) { + if (str == null || searchStr == null) { + return INDEX_NOT_FOUND; + } + if (startPos > str.length() - searchStr.length()) { + startPos = str.length() - searchStr.length(); + } + if (startPos < 0) { + return INDEX_NOT_FOUND; + } + if (searchStr.length() == 0) { + return startPos; + } + + for (int i = startPos; i >= 0; i--) { + if (CharSequenceUtils.regionMatches(str, true, i, searchStr, 0, searchStr.length())) { + return i; + } + } + return INDEX_NOT_FOUND; + } + + // Contains + //----------------------------------------------------------------------- + /** + *

Checks if CharSequence contains a search character, handling {@code null}. + * This method uses {@link String#indexOf(int)} if possible.

+ * + *

A {@code null} or empty ("") CharSequence will return {@code false}.

+ * + *
+     * StringUtils.contains(null, *)    = false
+     * StringUtils.contains("", *)      = false
+     * StringUtils.contains("abc", 'a') = true
+     * StringUtils.contains("abc", 'z') = false
+     * 
+ * + * @param seq the CharSequence to check, may be null + * @param searchChar the character to find + * @return true if the CharSequence contains the search character, + * false if not or {@code null} string input + * @since 2.0 + * @since 3.0 Changed signature from contains(String, int) to contains(CharSequence, int) + */ + public static boolean contains(CharSequence seq, int searchChar) { + if (isEmpty(seq)) { + return false; + } + return CharSequenceUtils.indexOf(seq, searchChar, 0) >= 0; + } + + /** + *

Checks if CharSequence contains a search CharSequence, handling {@code null}. + * This method uses {@link String#indexOf(String)} if possible.

+ * + *

A {@code null} CharSequence will return {@code false}.

+ * + *
+     * StringUtils.contains(null, *)     = false
+     * StringUtils.contains(*, null)     = false
+     * StringUtils.contains("", "")      = true
+     * StringUtils.contains("abc", "")   = true
+     * StringUtils.contains("abc", "a")  = true
+     * StringUtils.contains("abc", "z")  = false
+     * 
+ * + * @param seq the CharSequence to check, may be null + * @param searchSeq the CharSequence to find, may be null + * @return true if the CharSequence contains the search CharSequence, + * false if not or {@code null} string input + * @since 2.0 + * @since 3.0 Changed signature from contains(String, String) to contains(CharSequence, CharSequence) + */ + public static boolean contains(CharSequence seq, CharSequence searchSeq) { + if (seq == null || searchSeq == null) { + return false; + } + return CharSequenceUtils.indexOf(seq, searchSeq, 0) >= 0; + } + + /** + *

Checks if CharSequence contains a search CharSequence irrespective of case, + * handling {@code null}. Case-insensitivity is defined as by + * {@link String#equalsIgnoreCase(String)}. + * + *

A {@code null} CharSequence will return {@code false}.

+ * + *
+     * StringUtils.contains(null, *) = false
+     * StringUtils.contains(*, null) = false
+     * StringUtils.contains("", "") = true
+     * StringUtils.contains("abc", "") = true
+     * StringUtils.contains("abc", "a") = true
+     * StringUtils.contains("abc", "z") = false
+     * StringUtils.contains("abc", "A") = true
+     * StringUtils.contains("abc", "Z") = false
+     * 
+ * + * @param str the CharSequence to check, may be null + * @param searchStr the CharSequence to find, may be null + * @return true if the CharSequence contains the search CharSequence irrespective of + * case or false if not or {@code null} string input + * @since 3.0 Changed signature from containsIgnoreCase(String, String) to containsIgnoreCase(CharSequence, CharSequence) + */ + public static boolean containsIgnoreCase(CharSequence str, CharSequence searchStr) { + if (str == null || searchStr == null) { + return false; + } + int len = searchStr.length(); + int max = str.length() - len; + for (int i = 0; i <= max; i++) { + if (CharSequenceUtils.regionMatches(str, true, i, searchStr, 0, len)) { + return true; + } + } + return false; + } + + /** + * Check whether the given CharSequence contains any whitespace characters. + * @param seq the CharSequence to check (may be {@code null}) + * @return {@code true} if the CharSequence is not empty and + * contains at least 1 whitespace character + * @see java.lang.Character#isWhitespace + * @since 3.0 + */ + // From org.springframework.util.StringUtils, under Apache License 2.0 + public static boolean containsWhitespace(CharSequence seq) { + if (isEmpty(seq)) { + return false; + } + int strLen = seq.length(); + for (int i = 0; i < strLen; i++) { + if (Character.isWhitespace(seq.charAt(i))) { + return true; + } + } + return false; + } + + // IndexOfAny chars + //----------------------------------------------------------------------- + /** + *

Search a CharSequence to find the first index of any + * character in the given set of characters.

+ * + *

A {@code null} String will return {@code -1}. + * A {@code null} or zero length search array will return {@code -1}.

+ * + *
+     * StringUtils.indexOfAny(null, *)                = -1
+     * StringUtils.indexOfAny("", *)                  = -1
+     * StringUtils.indexOfAny(*, null)                = -1
+     * StringUtils.indexOfAny(*, [])                  = -1
+     * StringUtils.indexOfAny("zzabyycdxx",['z','a']) = 0
+     * StringUtils.indexOfAny("zzabyycdxx",['b','y']) = 3
+     * StringUtils.indexOfAny("aba", ['z'])           = -1
+     * 
+ * + * @param cs the CharSequence to check, may be null + * @param searchChars the chars to search for, may be null + * @return the index of any of the chars, -1 if no match or null input + * @since 2.0 + * @since 3.0 Changed signature from indexOfAny(String, char[]) to indexOfAny(CharSequence, char...) + */ + public static int indexOfAny(CharSequence cs, char... searchChars) { + if (isEmpty(cs) || ArrayUtils.isEmpty(searchChars)) { + return INDEX_NOT_FOUND; + } + int csLen = cs.length(); + int csLast = csLen - 1; + int searchLen = searchChars.length; + int searchLast = searchLen - 1; + for (int i = 0; i < csLen; i++) { + char ch = cs.charAt(i); + for (int j = 0; j < searchLen; j++) { + if (searchChars[j] == ch) { + if (i < csLast && j < searchLast && Character.isHighSurrogate(ch)) { + // ch is a supplementary character + if (searchChars[j + 1] == cs.charAt(i + 1)) { + return i; + } + } else { + return i; + } + } + } + } + return INDEX_NOT_FOUND; + } + + /** + *

Search a CharSequence to find the first index of any + * character in the given set of characters.

+ * + *

A {@code null} String will return {@code -1}. + * A {@code null} search string will return {@code -1}.

+ * + *
+     * StringUtils.indexOfAny(null, *)            = -1
+     * StringUtils.indexOfAny("", *)              = -1
+     * StringUtils.indexOfAny(*, null)            = -1
+     * StringUtils.indexOfAny(*, "")              = -1
+     * StringUtils.indexOfAny("zzabyycdxx", "za") = 0
+     * StringUtils.indexOfAny("zzabyycdxx", "by") = 3
+     * StringUtils.indexOfAny("aba","z")          = -1
+     * 
+ * + * @param cs the CharSequence to check, may be null + * @param searchChars the chars to search for, may be null + * @return the index of any of the chars, -1 if no match or null input + * @since 2.0 + * @since 3.0 Changed signature from indexOfAny(String, String) to indexOfAny(CharSequence, String) + */ + public static int indexOfAny(CharSequence cs, String searchChars) { + if (isEmpty(cs) || isEmpty(searchChars)) { + return INDEX_NOT_FOUND; + } + return indexOfAny(cs, searchChars.toCharArray()); + } + + // ContainsAny + //----------------------------------------------------------------------- + /** + *

Checks if the CharSequence contains any character in the given + * set of characters.

+ * + *

A {@code null} CharSequence will return {@code false}. + * A {@code null} or zero length search array will return {@code false}.

+ * + *
+     * StringUtils.containsAny(null, *)                = false
+     * StringUtils.containsAny("", *)                  = false
+     * StringUtils.containsAny(*, null)                = false
+     * StringUtils.containsAny(*, [])                  = false
+     * StringUtils.containsAny("zzabyycdxx",['z','a']) = true
+     * StringUtils.containsAny("zzabyycdxx",['b','y']) = true
+     * StringUtils.containsAny("aba", ['z'])           = false
+     * 
+ * + * @param cs the CharSequence to check, may be null + * @param searchChars the chars to search for, may be null + * @return the {@code true} if any of the chars are found, + * {@code false} if no match or null input + * @since 2.4 + * @since 3.0 Changed signature from containsAny(String, char[]) to containsAny(CharSequence, char...) + */ + public static boolean containsAny(CharSequence cs, char... searchChars) { + if (isEmpty(cs) || ArrayUtils.isEmpty(searchChars)) { + return false; + } + int csLength = cs.length(); + int searchLength = searchChars.length; + int csLast = csLength - 1; + int searchLast = searchLength - 1; + for (int i = 0; i < csLength; i++) { + char ch = cs.charAt(i); + for (int j = 0; j < searchLength; j++) { + if (searchChars[j] == ch) { + if (Character.isHighSurrogate(ch)) { + if (j == searchLast) { + // missing low surrogate, fine, like String.indexOf(String) + return true; + } + if (i < csLast && searchChars[j + 1] == cs.charAt(i + 1)) { + return true; + } + } else { + // ch is in the Basic Multilingual Plane + return true; + } + } + } + } + return false; + } + + /** + *

+ * Checks if the CharSequence contains any character in the given set of characters. + *

+ * + *

+ * A {@code null} CharSequence will return {@code false}. A {@code null} search CharSequence will return + * {@code false}. + *

+ * + *
+     * StringUtils.containsAny(null, *)            = false
+     * StringUtils.containsAny("", *)              = false
+     * StringUtils.containsAny(*, null)            = false
+     * StringUtils.containsAny(*, "")              = false
+     * StringUtils.containsAny("zzabyycdxx", "za") = true
+     * StringUtils.containsAny("zzabyycdxx", "by") = true
+     * StringUtils.containsAny("aba","z")          = false
+     * 
+ * + * @param cs + * the CharSequence to check, may be null + * @param searchChars + * the chars to search for, may be null + * @return the {@code true} if any of the chars are found, {@code false} if no match or null input + * @since 2.4 + * @since 3.0 Changed signature from containsAny(String, String) to containsAny(CharSequence, CharSequence) + */ + public static boolean containsAny(CharSequence cs, CharSequence searchChars) { + if (searchChars == null) { + return false; + } + return containsAny(cs, CharSequenceUtils.toCharArray(searchChars)); + } + + // IndexOfAnyBut chars + //----------------------------------------------------------------------- + /** + *

Searches a CharSequence to find the first index of any + * character not in the given set of characters.

+ * + *

A {@code null} CharSequence will return {@code -1}. + * A {@code null} or zero length search array will return {@code -1}.

+ * + *
+     * StringUtils.indexOfAnyBut(null, *)                              = -1
+     * StringUtils.indexOfAnyBut("", *)                                = -1
+     * StringUtils.indexOfAnyBut(*, null)                              = -1
+     * StringUtils.indexOfAnyBut(*, [])                                = -1
+     * StringUtils.indexOfAnyBut("zzabyycdxx", new char[] {'z', 'a'} ) = 3
+     * StringUtils.indexOfAnyBut("aba", new char[] {'z'} )             = 0
+     * StringUtils.indexOfAnyBut("aba", new char[] {'a', 'b'} )        = -1
+
+     * 
+ * + * @param cs the CharSequence to check, may be null + * @param searchChars the chars to search for, may be null + * @return the index of any of the chars, -1 if no match or null input + * @since 2.0 + * @since 3.0 Changed signature from indexOfAnyBut(String, char[]) to indexOfAnyBut(CharSequence, char...) + */ + public static int indexOfAnyBut(CharSequence cs, char... searchChars) { + if (isEmpty(cs) || ArrayUtils.isEmpty(searchChars)) { + return INDEX_NOT_FOUND; + } + int csLen = cs.length(); + int csLast = csLen - 1; + int searchLen = searchChars.length; + int searchLast = searchLen - 1; + outer: + for (int i = 0; i < csLen; i++) { + char ch = cs.charAt(i); + for (int j = 0; j < searchLen; j++) { + if (searchChars[j] == ch) { + if (i < csLast && j < searchLast && Character.isHighSurrogate(ch)) { + if (searchChars[j + 1] == cs.charAt(i + 1)) { + continue outer; + } + } else { + continue outer; + } + } + } + return i; + } + return INDEX_NOT_FOUND; + } + + /** + *

Search a CharSequence to find the first index of any + * character not in the given set of characters.

+ * + *

A {@code null} CharSequence will return {@code -1}. + * A {@code null} or empty search string will return {@code -1}.

+ * + *
+     * StringUtils.indexOfAnyBut(null, *)            = -1
+     * StringUtils.indexOfAnyBut("", *)              = -1
+     * StringUtils.indexOfAnyBut(*, null)            = -1
+     * StringUtils.indexOfAnyBut(*, "")              = -1
+     * StringUtils.indexOfAnyBut("zzabyycdxx", "za") = 3
+     * StringUtils.indexOfAnyBut("zzabyycdxx", "")   = -1
+     * StringUtils.indexOfAnyBut("aba","ab")         = -1
+     * 
+ * + * @param seq the CharSequence to check, may be null + * @param searchChars the chars to search for, may be null + * @return the index of any of the chars, -1 if no match or null input + * @since 2.0 + * @since 3.0 Changed signature from indexOfAnyBut(String, String) to indexOfAnyBut(CharSequence, CharSequence) + */ + public static int indexOfAnyBut(CharSequence seq, CharSequence searchChars) { + if (isEmpty(seq) || isEmpty(searchChars)) { + return INDEX_NOT_FOUND; + } + int strLen = seq.length(); + for (int i = 0; i < strLen; i++) { + char ch = seq.charAt(i); + boolean chFound = CharSequenceUtils.indexOf(searchChars, ch, 0) >= 0; + if (i + 1 < strLen && Character.isHighSurrogate(ch)) { + char ch2 = seq.charAt(i + 1); + if (chFound && CharSequenceUtils.indexOf(searchChars, ch2, 0) < 0) { + return i; + } + } else { + if (!chFound) { + return i; + } + } + } + return INDEX_NOT_FOUND; + } + + // ContainsOnly + //----------------------------------------------------------------------- + /** + *

Checks if the CharSequence contains only certain characters.

+ * + *

A {@code null} CharSequence will return {@code false}. + * A {@code null} valid character array will return {@code false}. + * An empty CharSequence (length()=0) always returns {@code true}.

+ * + *
+     * StringUtils.containsOnly(null, *)       = false
+     * StringUtils.containsOnly(*, null)       = false
+     * StringUtils.containsOnly("", *)         = true
+     * StringUtils.containsOnly("ab", '')      = false
+     * StringUtils.containsOnly("abab", 'abc') = true
+     * StringUtils.containsOnly("ab1", 'abc')  = false
+     * StringUtils.containsOnly("abz", 'abc')  = false
+     * 
+ * + * @param cs the String to check, may be null + * @param valid an array of valid chars, may be null + * @return true if it only contains valid chars and is non-null + * @since 3.0 Changed signature from containsOnly(String, char[]) to containsOnly(CharSequence, char...) + */ + public static boolean containsOnly(CharSequence cs, char... valid) { + // All these pre-checks are to maintain API with an older version + if (valid == null || cs == null) { + return false; + } + if (cs.length() == 0) { + return true; + } + if (valid.length == 0) { + return false; + } + return indexOfAnyBut(cs, valid) == INDEX_NOT_FOUND; + } + + /** + *

Checks if the CharSequence contains only certain characters.

+ * + *

A {@code null} CharSequence will return {@code false}. + * A {@code null} valid character String will return {@code false}. + * An empty String (length()=0) always returns {@code true}.

+ * + *
+     * StringUtils.containsOnly(null, *)       = false
+     * StringUtils.containsOnly(*, null)       = false
+     * StringUtils.containsOnly("", *)         = true
+     * StringUtils.containsOnly("ab", "")      = false
+     * StringUtils.containsOnly("abab", "abc") = true
+     * StringUtils.containsOnly("ab1", "abc")  = false
+     * StringUtils.containsOnly("abz", "abc")  = false
+     * 
+ * + * @param cs the CharSequence to check, may be null + * @param validChars a String of valid chars, may be null + * @return true if it only contains valid chars and is non-null + * @since 2.0 + * @since 3.0 Changed signature from containsOnly(String, String) to containsOnly(CharSequence, String) + */ + public static boolean containsOnly(CharSequence cs, String validChars) { + if (cs == null || validChars == null) { + return false; + } + return containsOnly(cs, validChars.toCharArray()); + } + + // ContainsNone + //----------------------------------------------------------------------- + /** + *

Checks that the CharSequence does not contain certain characters.

+ * + *

A {@code null} CharSequence will return {@code true}. + * A {@code null} invalid character array will return {@code true}. + * An empty CharSequence (length()=0) always returns true.

+ * + *
+     * StringUtils.containsNone(null, *)       = true
+     * StringUtils.containsNone(*, null)       = true
+     * StringUtils.containsNone("", *)         = true
+     * StringUtils.containsNone("ab", '')      = true
+     * StringUtils.containsNone("abab", 'xyz') = true
+     * StringUtils.containsNone("ab1", 'xyz')  = true
+     * StringUtils.containsNone("abz", 'xyz')  = false
+     * 
+ * + * @param cs the CharSequence to check, may be null + * @param searchChars an array of invalid chars, may be null + * @return true if it contains none of the invalid chars, or is null + * @since 2.0 + * @since 3.0 Changed signature from containsNone(String, char[]) to containsNone(CharSequence, char...) + */ + public static boolean containsNone(CharSequence cs, char... searchChars) { + if (cs == null || searchChars == null) { + return true; + } + int csLen = cs.length(); + int csLast = csLen - 1; + int searchLen = searchChars.length; + int searchLast = searchLen - 1; + for (int i = 0; i < csLen; i++) { + char ch = cs.charAt(i); + for (int j = 0; j < searchLen; j++) { + if (searchChars[j] == ch) { + if (Character.isHighSurrogate(ch)) { + if (j == searchLast) { + // missing low surrogate, fine, like String.indexOf(String) + return false; + } + if (i < csLast && searchChars[j + 1] == cs.charAt(i + 1)) { + return false; + } + } else { + // ch is in the Basic Multilingual Plane + return false; + } + } + } + } + return true; + } + + /** + *

Checks that the CharSequence does not contain certain characters.

+ * + *

A {@code null} CharSequence will return {@code true}. + * A {@code null} invalid character array will return {@code true}. + * An empty String ("") always returns true.

+ * + *
+     * StringUtils.containsNone(null, *)       = true
+     * StringUtils.containsNone(*, null)       = true
+     * StringUtils.containsNone("", *)         = true
+     * StringUtils.containsNone("ab", "")      = true
+     * StringUtils.containsNone("abab", "xyz") = true
+     * StringUtils.containsNone("ab1", "xyz")  = true
+     * StringUtils.containsNone("abz", "xyz")  = false
+     * 
+ * + * @param cs the CharSequence to check, may be null + * @param invalidChars a String of invalid chars, may be null + * @return true if it contains none of the invalid chars, or is null + * @since 2.0 + * @since 3.0 Changed signature from containsNone(String, String) to containsNone(CharSequence, String) + */ + public static boolean containsNone(CharSequence cs, String invalidChars) { + if (cs == null || invalidChars == null) { + return true; + } + return containsNone(cs, invalidChars.toCharArray()); + } + + // IndexOfAny strings + //----------------------------------------------------------------------- + /** + *

Find the first index of any of a set of potential substrings.

+ * + *

A {@code null} CharSequence will return {@code -1}. + * A {@code null} or zero length search array will return {@code -1}. + * A {@code null} search array entry will be ignored, but a search + * array containing "" will return {@code 0} if {@code str} is not + * null. This method uses {@link String#indexOf(String)} if possible.

+ * + *
+     * StringUtils.indexOfAny(null, *)                     = -1
+     * StringUtils.indexOfAny(*, null)                     = -1
+     * StringUtils.indexOfAny(*, [])                       = -1
+     * StringUtils.indexOfAny("zzabyycdxx", ["ab","cd"])   = 2
+     * StringUtils.indexOfAny("zzabyycdxx", ["cd","ab"])   = 2
+     * StringUtils.indexOfAny("zzabyycdxx", ["mn","op"])   = -1
+     * StringUtils.indexOfAny("zzabyycdxx", ["zab","aby"]) = 1
+     * StringUtils.indexOfAny("zzabyycdxx", [""])          = 0
+     * StringUtils.indexOfAny("", [""])                    = 0
+     * StringUtils.indexOfAny("", ["a"])                   = -1
+     * 
+ * + * @param str the CharSequence to check, may be null + * @param searchStrs the CharSequences to search for, may be null + * @return the first index of any of the searchStrs in str, -1 if no match + * @since 3.0 Changed signature from indexOfAny(String, String[]) to indexOfAny(CharSequence, CharSequence...) + */ + public static int indexOfAny(CharSequence str, CharSequence... searchStrs) { + if (str == null || searchStrs == null) { + return INDEX_NOT_FOUND; + } + int sz = searchStrs.length; + + // String's can't have a MAX_VALUEth index. + int ret = Integer.MAX_VALUE; + + int tmp = 0; + for (int i = 0; i < sz; i++) { + CharSequence search = searchStrs[i]; + if (search == null) { + continue; + } + tmp = CharSequenceUtils.indexOf(str, search, 0); + if (tmp == INDEX_NOT_FOUND) { + continue; + } + + if (tmp < ret) { + ret = tmp; + } + } + + return ret == Integer.MAX_VALUE ? INDEX_NOT_FOUND : ret; + } + + /** + *

Find the latest index of any of a set of potential substrings.

+ * + *

A {@code null} CharSequence will return {@code -1}. + * A {@code null} search array will return {@code -1}. + * A {@code null} or zero length search array entry will be ignored, + * but a search array containing "" will return the length of {@code str} + * if {@code str} is not null. This method uses {@link String#indexOf(String)} if possible

+ * + *
+     * StringUtils.lastIndexOfAny(null, *)                   = -1
+     * StringUtils.lastIndexOfAny(*, null)                   = -1
+     * StringUtils.lastIndexOfAny(*, [])                     = -1
+     * StringUtils.lastIndexOfAny(*, [null])                 = -1
+     * StringUtils.lastIndexOfAny("zzabyycdxx", ["ab","cd"]) = 6
+     * StringUtils.lastIndexOfAny("zzabyycdxx", ["cd","ab"]) = 6
+     * StringUtils.lastIndexOfAny("zzabyycdxx", ["mn","op"]) = -1
+     * StringUtils.lastIndexOfAny("zzabyycdxx", ["mn","op"]) = -1
+     * StringUtils.lastIndexOfAny("zzabyycdxx", ["mn",""])   = 10
+     * 
+ * + * @param str the CharSequence to check, may be null + * @param searchStrs the CharSequences to search for, may be null + * @return the last index of any of the CharSequences, -1 if no match + * @since 3.0 Changed signature from lastIndexOfAny(String, String[]) to lastIndexOfAny(CharSequence, CharSequence) + */ + public static int lastIndexOfAny(CharSequence str, CharSequence... searchStrs) { + if (str == null || searchStrs == null) { + return INDEX_NOT_FOUND; + } + int sz = searchStrs.length; + int ret = INDEX_NOT_FOUND; + int tmp = 0; + for (int i = 0; i < sz; i++) { + CharSequence search = searchStrs[i]; + if (search == null) { + continue; + } + tmp = CharSequenceUtils.lastIndexOf(str, search, str.length()); + if (tmp > ret) { + ret = tmp; + } + } + return ret; + } + + // Substring + //----------------------------------------------------------------------- + /** + *

Gets a substring from the specified String avoiding exceptions.

+ * + *

A negative start position can be used to start {@code n} + * characters from the end of the String.

+ * + *

A {@code null} String will return {@code null}. + * An empty ("") String will return "".

+ * + *
+     * StringUtils.substring(null, *)   = null
+     * StringUtils.substring("", *)     = ""
+     * StringUtils.substring("abc", 0)  = "abc"
+     * StringUtils.substring("abc", 2)  = "c"
+     * StringUtils.substring("abc", 4)  = ""
+     * StringUtils.substring("abc", -2) = "bc"
+     * StringUtils.substring("abc", -4) = "abc"
+     * 
+ * + * @param str the String to get the substring from, may be null + * @param start the position to start from, negative means + * count back from the end of the String by this many characters + * @return substring from start position, {@code null} if null String input + */ + public static String substring(String str, int start) { + if (str == null) { + return null; + } + + // handle negatives, which means last n characters + if (start < 0) { + start = str.length() + start; // remember start is negative + } + + if (start < 0) { + start = 0; + } + if (start > str.length()) { + return EMPTY; + } + + return str.substring(start); + } + + /** + *

Gets a substring from the specified String avoiding exceptions.

+ * + *

A negative start position can be used to start/end {@code n} + * characters from the end of the String.

+ * + *

The returned substring starts with the character in the {@code start} + * position and ends before the {@code end} position. All position counting is + * zero-based -- i.e., to start at the beginning of the string use + * {@code start = 0}. Negative start and end positions can be used to + * specify offsets relative to the end of the String.

+ * + *

If {@code start} is not strictly to the left of {@code end}, "" + * is returned.

+ * + *
+     * StringUtils.substring(null, *, *)    = null
+     * StringUtils.substring("", * ,  *)    = "";
+     * StringUtils.substring("abc", 0, 2)   = "ab"
+     * StringUtils.substring("abc", 2, 0)   = ""
+     * StringUtils.substring("abc", 2, 4)   = "c"
+     * StringUtils.substring("abc", 4, 6)   = ""
+     * StringUtils.substring("abc", 2, 2)   = ""
+     * StringUtils.substring("abc", -2, -1) = "b"
+     * StringUtils.substring("abc", -4, 2)  = "ab"
+     * 
+ * + * @param str the String to get the substring from, may be null + * @param start the position to start from, negative means + * count back from the end of the String by this many characters + * @param end the position to end at (exclusive), negative means + * count back from the end of the String by this many characters + * @return substring from start position to end position, + * {@code null} if null String input + */ + public static String substring(String str, int start, int end) { + if (str == null) { + return null; + } + + // handle negatives + if (end < 0) { + end = str.length() + end; // remember end is negative + } + if (start < 0) { + start = str.length() + start; // remember start is negative + } + + // check length next + if (end > str.length()) { + end = str.length(); + } + + // if start is greater than end, return "" + if (start > end) { + return EMPTY; + } + + if (start < 0) { + start = 0; + } + if (end < 0) { + end = 0; + } + + return str.substring(start, end); + } + + // Left/Right/Mid + //----------------------------------------------------------------------- + /** + *

Gets the leftmost {@code len} characters of a String.

+ * + *

If {@code len} characters are not available, or the + * String is {@code null}, the String will be returned without + * an exception. An empty String is returned if len is negative.

+ * + *
+     * StringUtils.left(null, *)    = null
+     * StringUtils.left(*, -ve)     = ""
+     * StringUtils.left("", *)      = ""
+     * StringUtils.left("abc", 0)   = ""
+     * StringUtils.left("abc", 2)   = "ab"
+     * StringUtils.left("abc", 4)   = "abc"
+     * 
+ * + * @param str the String to get the leftmost characters from, may be null + * @param len the length of the required String + * @return the leftmost characters, {@code null} if null String input + */ + public static String left(String str, int len) { + if (str == null) { + return null; + } + if (len < 0) { + return EMPTY; + } + if (str.length() <= len) { + return str; + } + return str.substring(0, len); + } + + /** + *

Gets the rightmost {@code len} characters of a String.

+ * + *

If {@code len} characters are not available, or the String + * is {@code null}, the String will be returned without an + * an exception. An empty String is returned if len is negative.

+ * + *
+     * StringUtils.right(null, *)    = null
+     * StringUtils.right(*, -ve)     = ""
+     * StringUtils.right("", *)      = ""
+     * StringUtils.right("abc", 0)   = ""
+     * StringUtils.right("abc", 2)   = "bc"
+     * StringUtils.right("abc", 4)   = "abc"
+     * 
+ * + * @param str the String to get the rightmost characters from, may be null + * @param len the length of the required String + * @return the rightmost characters, {@code null} if null String input + */ + public static String right(String str, int len) { + if (str == null) { + return null; + } + if (len < 0) { + return EMPTY; + } + if (str.length() <= len) { + return str; + } + return str.substring(str.length() - len); + } + + /** + *

Gets {@code len} characters from the middle of a String.

+ * + *

If {@code len} characters are not available, the remainder + * of the String will be returned without an exception. If the + * String is {@code null}, {@code null} will be returned. + * An empty String is returned if len is negative or exceeds the + * length of {@code str}.

+ * + *
+     * StringUtils.mid(null, *, *)    = null
+     * StringUtils.mid(*, *, -ve)     = ""
+     * StringUtils.mid("", 0, *)      = ""
+     * StringUtils.mid("abc", 0, 2)   = "ab"
+     * StringUtils.mid("abc", 0, 4)   = "abc"
+     * StringUtils.mid("abc", 2, 4)   = "c"
+     * StringUtils.mid("abc", 4, 2)   = ""
+     * StringUtils.mid("abc", -2, 2)  = "ab"
+     * 
+ * + * @param str the String to get the characters from, may be null + * @param pos the position to start from, negative treated as zero + * @param len the length of the required String + * @return the middle characters, {@code null} if null String input + */ + public static String mid(String str, int pos, int len) { + if (str == null) { + return null; + } + if (len < 0 || pos > str.length()) { + return EMPTY; + } + if (pos < 0) { + pos = 0; + } + if (str.length() <= pos + len) { + return str.substring(pos); + } + return str.substring(pos, pos + len); + } + + // SubStringAfter/SubStringBefore + //----------------------------------------------------------------------- + /** + *

Gets the substring before the first occurrence of a separator. + * The separator is not returned.

+ * + *

A {@code null} string input will return {@code null}. + * An empty ("") string input will return the empty string. + * A {@code null} separator will return the input string.

+ * + *

If nothing is found, the string input is returned.

+ * + *
+     * StringUtils.substringBefore(null, *)      = null
+     * StringUtils.substringBefore("", *)        = ""
+     * StringUtils.substringBefore("abc", "a")   = ""
+     * StringUtils.substringBefore("abcba", "b") = "a"
+     * StringUtils.substringBefore("abc", "c")   = "ab"
+     * StringUtils.substringBefore("abc", "d")   = "abc"
+     * StringUtils.substringBefore("abc", "")    = ""
+     * StringUtils.substringBefore("abc", null)  = "abc"
+     * 
+ * + * @param str the String to get a substring from, may be null + * @param separator the String to search for, may be null + * @return the substring before the first occurrence of the separator, + * {@code null} if null String input + * @since 2.0 + */ + public static String substringBefore(String str, String separator) { + if (isEmpty(str) || separator == null) { + return str; + } + if (separator.length() == 0) { + return EMPTY; + } + int pos = str.indexOf(separator); + if (pos == INDEX_NOT_FOUND) { + return str; + } + return str.substring(0, pos); + } + + /** + *

Gets the substring after the first occurrence of a separator. + * The separator is not returned.

+ * + *

A {@code null} string input will return {@code null}. + * An empty ("") string input will return the empty string. + * A {@code null} separator will return the empty string if the + * input string is not {@code null}.

+ * + *

If nothing is found, the empty string is returned.

+ * + *
+     * StringUtils.substringAfter(null, *)      = null
+     * StringUtils.substringAfter("", *)        = ""
+     * StringUtils.substringAfter(*, null)      = ""
+     * StringUtils.substringAfter("abc", "a")   = "bc"
+     * StringUtils.substringAfter("abcba", "b") = "cba"
+     * StringUtils.substringAfter("abc", "c")   = ""
+     * StringUtils.substringAfter("abc", "d")   = ""
+     * StringUtils.substringAfter("abc", "")    = "abc"
+     * 
+ * + * @param str the String to get a substring from, may be null + * @param separator the String to search for, may be null + * @return the substring after the first occurrence of the separator, + * {@code null} if null String input + * @since 2.0 + */ + public static String substringAfter(String str, String separator) { + if (isEmpty(str)) { + return str; + } + if (separator == null) { + return EMPTY; + } + int pos = str.indexOf(separator); + if (pos == INDEX_NOT_FOUND) { + return EMPTY; + } + return str.substring(pos + separator.length()); + } + + /** + *

Gets the substring before the last occurrence of a separator. + * The separator is not returned.

+ * + *

A {@code null} string input will return {@code null}. + * An empty ("") string input will return the empty string. + * An empty or {@code null} separator will return the input string.

+ * + *

If nothing is found, the string input is returned.

+ * + *
+     * StringUtils.substringBeforeLast(null, *)      = null
+     * StringUtils.substringBeforeLast("", *)        = ""
+     * StringUtils.substringBeforeLast("abcba", "b") = "abc"
+     * StringUtils.substringBeforeLast("abc", "c")   = "ab"
+     * StringUtils.substringBeforeLast("a", "a")     = ""
+     * StringUtils.substringBeforeLast("a", "z")     = "a"
+     * StringUtils.substringBeforeLast("a", null)    = "a"
+     * StringUtils.substringBeforeLast("a", "")      = "a"
+     * 
+ * + * @param str the String to get a substring from, may be null + * @param separator the String to search for, may be null + * @return the substring before the last occurrence of the separator, + * {@code null} if null String input + * @since 2.0 + */ + public static String substringBeforeLast(String str, String separator) { + if (isEmpty(str) || isEmpty(separator)) { + return str; + } + int pos = str.lastIndexOf(separator); + if (pos == INDEX_NOT_FOUND) { + return str; + } + return str.substring(0, pos); + } + + /** + *

Gets the substring after the last occurrence of a separator. + * The separator is not returned.

+ * + *

A {@code null} string input will return {@code null}. + * An empty ("") string input will return the empty string. + * An empty or {@code null} separator will return the empty string if + * the input string is not {@code null}.

+ * + *

If nothing is found, the empty string is returned.

+ * + *
+     * StringUtils.substringAfterLast(null, *)      = null
+     * StringUtils.substringAfterLast("", *)        = ""
+     * StringUtils.substringAfterLast(*, "")        = ""
+     * StringUtils.substringAfterLast(*, null)      = ""
+     * StringUtils.substringAfterLast("abc", "a")   = "bc"
+     * StringUtils.substringAfterLast("abcba", "b") = "a"
+     * StringUtils.substringAfterLast("abc", "c")   = ""
+     * StringUtils.substringAfterLast("a", "a")     = ""
+     * StringUtils.substringAfterLast("a", "z")     = ""
+     * 
+ * + * @param str the String to get a substring from, may be null + * @param separator the String to search for, may be null + * @return the substring after the last occurrence of the separator, + * {@code null} if null String input + * @since 2.0 + */ + public static String substringAfterLast(String str, String separator) { + if (isEmpty(str)) { + return str; + } + if (isEmpty(separator)) { + return EMPTY; + } + int pos = str.lastIndexOf(separator); + if (pos == INDEX_NOT_FOUND || pos == str.length() - separator.length()) { + return EMPTY; + } + return str.substring(pos + separator.length()); + } + + // Substring between + //----------------------------------------------------------------------- + /** + *

Gets the String that is nested in between two instances of the + * same String.

+ * + *

A {@code null} input String returns {@code null}. + * A {@code null} tag returns {@code null}.

+ * + *
+     * StringUtils.substringBetween(null, *)            = null
+     * StringUtils.substringBetween("", "")             = ""
+     * StringUtils.substringBetween("", "tag")          = null
+     * StringUtils.substringBetween("tagabctag", null)  = null
+     * StringUtils.substringBetween("tagabctag", "")    = ""
+     * StringUtils.substringBetween("tagabctag", "tag") = "abc"
+     * 
+ * + * @param str the String containing the substring, may be null + * @param tag the String before and after the substring, may be null + * @return the substring, {@code null} if no match + * @since 2.0 + */ + public static String substringBetween(String str, String tag) { + return substringBetween(str, tag, tag); + } + + /** + *

Gets the String that is nested in between two Strings. + * Only the first match is returned.

+ * + *

A {@code null} input String returns {@code null}. + * A {@code null} open/close returns {@code null} (no match). + * An empty ("") open and close returns an empty string.

+ * + *
+     * StringUtils.substringBetween("wx[b]yz", "[", "]") = "b"
+     * StringUtils.substringBetween(null, *, *)          = null
+     * StringUtils.substringBetween(*, null, *)          = null
+     * StringUtils.substringBetween(*, *, null)          = null
+     * StringUtils.substringBetween("", "", "")          = ""
+     * StringUtils.substringBetween("", "", "]")         = null
+     * StringUtils.substringBetween("", "[", "]")        = null
+     * StringUtils.substringBetween("yabcz", "", "")     = ""
+     * StringUtils.substringBetween("yabcz", "y", "z")   = "abc"
+     * StringUtils.substringBetween("yabczyabcz", "y", "z")   = "abc"
+     * 
+ * + * @param str the String containing the substring, may be null + * @param open the String before the substring, may be null + * @param close the String after the substring, may be null + * @return the substring, {@code null} if no match + * @since 2.0 + */ + public static String substringBetween(String str, String open, String close) { + if (str == null || open == null || close == null) { + return null; + } + int start = str.indexOf(open); + if (start != INDEX_NOT_FOUND) { + int end = str.indexOf(close, start + open.length()); + if (end != INDEX_NOT_FOUND) { + return str.substring(start + open.length(), end); + } + } + return null; + } + + /** + *

Searches a String for substrings delimited by a start and end tag, + * returning all matching substrings in an array.

+ * + *

A {@code null} input String returns {@code null}. + * A {@code null} open/close returns {@code null} (no match). + * An empty ("") open/close returns {@code null} (no match).

+ * + *
+     * StringUtils.substringsBetween("[a][b][c]", "[", "]") = ["a","b","c"]
+     * StringUtils.substringsBetween(null, *, *)            = null
+     * StringUtils.substringsBetween(*, null, *)            = null
+     * StringUtils.substringsBetween(*, *, null)            = null
+     * StringUtils.substringsBetween("", "[", "]")          = []
+     * 
+ * + * @param str the String containing the substrings, null returns null, empty returns empty + * @param open the String identifying the start of the substring, empty returns null + * @param close the String identifying the end of the substring, empty returns null + * @return a String Array of substrings, or {@code null} if no match + * @since 2.3 + */ + public static String[] substringsBetween(String str, String open, String close) { + if (str == null || isEmpty(open) || isEmpty(close)) { + return null; + } + int strLen = str.length(); + if (strLen == 0) { + return ArrayUtils.EMPTY_STRING_ARRAY; + } + int closeLen = close.length(); + int openLen = open.length(); + List list = new ArrayList(); + int pos = 0; + while (pos < strLen - closeLen) { + int start = str.indexOf(open, pos); + if (start < 0) { + break; + } + start += openLen; + int end = str.indexOf(close, start); + if (end < 0) { + break; + } + list.add(str.substring(start, end)); + pos = end + closeLen; + } + if (list.isEmpty()) { + return null; + } + return list.toArray(new String [list.size()]); + } + + // Nested extraction + //----------------------------------------------------------------------- + + // Splitting + //----------------------------------------------------------------------- + /** + *

Splits the provided text into an array, using whitespace as the + * separator. + * Whitespace is defined by {@link Character#isWhitespace(char)}.

+ * + *

The separator is not included in the returned String array. + * Adjacent separators are treated as one separator. + * For more control over the split use the StrTokenizer class.

+ * + *

A {@code null} input String returns {@code null}.

+ * + *
+     * StringUtils.split(null)       = null
+     * StringUtils.split("")         = []
+     * StringUtils.split("abc def")  = ["abc", "def"]
+     * StringUtils.split("abc  def") = ["abc", "def"]
+     * StringUtils.split(" abc ")    = ["abc"]
+     * 
+ * + * @param str the String to parse, may be null + * @return an array of parsed Strings, {@code null} if null String input + */ + public static String[] split(String str) { + return split(str, null, -1); + } + + /** + *

Splits the provided text into an array, separator specified. + * This is an alternative to using StringTokenizer.

+ * + *

The separator is not included in the returned String array. + * Adjacent separators are treated as one separator. + * For more control over the split use the StrTokenizer class.

+ * + *

A {@code null} input String returns {@code null}.

+ * + *
+     * StringUtils.split(null, *)         = null
+     * StringUtils.split("", *)           = []
+     * StringUtils.split("a.b.c", '.')    = ["a", "b", "c"]
+     * StringUtils.split("a..b.c", '.')   = ["a", "b", "c"]
+     * StringUtils.split("a:b:c", '.')    = ["a:b:c"]
+     * StringUtils.split("a b c", ' ')    = ["a", "b", "c"]
+     * 
+ * + * @param str the String to parse, may be null + * @param separatorChar the character used as the delimiter + * @return an array of parsed Strings, {@code null} if null String input + * @since 2.0 + */ + public static String[] split(String str, char separatorChar) { + return splitWorker(str, separatorChar, false); + } + + /** + *

Splits the provided text into an array, separators specified. + * This is an alternative to using StringTokenizer.

+ * + *

The separator is not included in the returned String array. + * Adjacent separators are treated as one separator. + * For more control over the split use the StrTokenizer class.

+ * + *

A {@code null} input String returns {@code null}. + * A {@code null} separatorChars splits on whitespace.

+ * + *
+     * StringUtils.split(null, *)         = null
+     * StringUtils.split("", *)           = []
+     * StringUtils.split("abc def", null) = ["abc", "def"]
+     * StringUtils.split("abc def", " ")  = ["abc", "def"]
+     * StringUtils.split("abc  def", " ") = ["abc", "def"]
+     * StringUtils.split("ab:cd:ef", ":") = ["ab", "cd", "ef"]
+     * 
+ * + * @param str the String to parse, may be null + * @param separatorChars the characters used as the delimiters, + * {@code null} splits on whitespace + * @return an array of parsed Strings, {@code null} if null String input + */ + public static String[] split(String str, String separatorChars) { + return splitWorker(str, separatorChars, -1, false); + } + + /** + *

Splits the provided text into an array with a maximum length, + * separators specified.

+ * + *

The separator is not included in the returned String array. + * Adjacent separators are treated as one separator.

+ * + *

A {@code null} input String returns {@code null}. + * A {@code null} separatorChars splits on whitespace.

+ * + *

If more than {@code max} delimited substrings are found, the last + * returned string includes all characters after the first {@code max - 1} + * returned strings (including separator characters).

+ * + *
+     * StringUtils.split(null, *, *)            = null
+     * StringUtils.split("", *, *)              = []
+     * StringUtils.split("ab cd ef", null, 0)   = ["ab", "cd", "ef"]
+     * StringUtils.split("ab   cd ef", null, 0) = ["ab", "cd", "ef"]
+     * StringUtils.split("ab:cd:ef", ":", 0)    = ["ab", "cd", "ef"]
+     * StringUtils.split("ab:cd:ef", ":", 2)    = ["ab", "cd:ef"]
+     * 
+ * + * @param str the String to parse, may be null + * @param separatorChars the characters used as the delimiters, + * {@code null} splits on whitespace + * @param max the maximum number of elements to include in the + * array. A zero or negative value implies no limit + * @return an array of parsed Strings, {@code null} if null String input + */ + public static String[] split(String str, String separatorChars, int max) { + return splitWorker(str, separatorChars, max, false); + } + + /** + *

Splits the provided text into an array, separator string specified.

+ * + *

The separator(s) will not be included in the returned String array. + * Adjacent separators are treated as one separator.

+ * + *

A {@code null} input String returns {@code null}. + * A {@code null} separator splits on whitespace.

+ * + *
+     * StringUtils.splitByWholeSeparator(null, *)               = null
+     * StringUtils.splitByWholeSeparator("", *)                 = []
+     * StringUtils.splitByWholeSeparator("ab de fg", null)      = ["ab", "de", "fg"]
+     * StringUtils.splitByWholeSeparator("ab   de fg", null)    = ["ab", "de", "fg"]
+     * StringUtils.splitByWholeSeparator("ab:cd:ef", ":")       = ["ab", "cd", "ef"]
+     * StringUtils.splitByWholeSeparator("ab-!-cd-!-ef", "-!-") = ["ab", "cd", "ef"]
+     * 
+ * + * @param str the String to parse, may be null + * @param separator String containing the String to be used as a delimiter, + * {@code null} splits on whitespace + * @return an array of parsed Strings, {@code null} if null String was input + */ + public static String[] splitByWholeSeparator(String str, String separator) { + return splitByWholeSeparatorWorker( str, separator, -1, false ) ; + } + + /** + *

Splits the provided text into an array, separator string specified. + * Returns a maximum of {@code max} substrings.

+ * + *

The separator(s) will not be included in the returned String array. + * Adjacent separators are treated as one separator.

+ * + *

A {@code null} input String returns {@code null}. + * A {@code null} separator splits on whitespace.

+ * + *
+     * StringUtils.splitByWholeSeparator(null, *, *)               = null
+     * StringUtils.splitByWholeSeparator("", *, *)                 = []
+     * StringUtils.splitByWholeSeparator("ab de fg", null, 0)      = ["ab", "de", "fg"]
+     * StringUtils.splitByWholeSeparator("ab   de fg", null, 0)    = ["ab", "de", "fg"]
+     * StringUtils.splitByWholeSeparator("ab:cd:ef", ":", 2)       = ["ab", "cd:ef"]
+     * StringUtils.splitByWholeSeparator("ab-!-cd-!-ef", "-!-", 5) = ["ab", "cd", "ef"]
+     * StringUtils.splitByWholeSeparator("ab-!-cd-!-ef", "-!-", 2) = ["ab", "cd-!-ef"]
+     * 
+ * + * @param str the String to parse, may be null + * @param separator String containing the String to be used as a delimiter, + * {@code null} splits on whitespace + * @param max the maximum number of elements to include in the returned + * array. A zero or negative value implies no limit. + * @return an array of parsed Strings, {@code null} if null String was input + */ + public static String[] splitByWholeSeparator( String str, String separator, int max ) { + return splitByWholeSeparatorWorker(str, separator, max, false); + } + + /** + *

Splits the provided text into an array, separator string specified.

+ * + *

The separator is not included in the returned String array. + * Adjacent separators are treated as separators for empty tokens. + * For more control over the split use the StrTokenizer class.

+ * + *

A {@code null} input String returns {@code null}. + * A {@code null} separator splits on whitespace.

+ * + *
+     * StringUtils.splitByWholeSeparatorPreserveAllTokens(null, *)               = null
+     * StringUtils.splitByWholeSeparatorPreserveAllTokens("", *)                 = []
+     * StringUtils.splitByWholeSeparatorPreserveAllTokens("ab de fg", null)      = ["ab", "de", "fg"]
+     * StringUtils.splitByWholeSeparatorPreserveAllTokens("ab   de fg", null)    = ["ab", "", "", "de", "fg"]
+     * StringUtils.splitByWholeSeparatorPreserveAllTokens("ab:cd:ef", ":")       = ["ab", "cd", "ef"]
+     * StringUtils.splitByWholeSeparatorPreserveAllTokens("ab-!-cd-!-ef", "-!-") = ["ab", "cd", "ef"]
+     * 
+ * + * @param str the String to parse, may be null + * @param separator String containing the String to be used as a delimiter, + * {@code null} splits on whitespace + * @return an array of parsed Strings, {@code null} if null String was input + * @since 2.4 + */ + public static String[] splitByWholeSeparatorPreserveAllTokens(String str, String separator) { + return splitByWholeSeparatorWorker(str, separator, -1, true); + } + + /** + *

Splits the provided text into an array, separator string specified. + * Returns a maximum of {@code max} substrings.

+ * + *

The separator is not included in the returned String array. + * Adjacent separators are treated as separators for empty tokens. + * For more control over the split use the StrTokenizer class.

+ * + *

A {@code null} input String returns {@code null}. + * A {@code null} separator splits on whitespace.

+ * + *
+     * StringUtils.splitByWholeSeparatorPreserveAllTokens(null, *, *)               = null
+     * StringUtils.splitByWholeSeparatorPreserveAllTokens("", *, *)                 = []
+     * StringUtils.splitByWholeSeparatorPreserveAllTokens("ab de fg", null, 0)      = ["ab", "de", "fg"]
+     * StringUtils.splitByWholeSeparatorPreserveAllTokens("ab   de fg", null, 0)    = ["ab", "", "", "de", "fg"]
+     * StringUtils.splitByWholeSeparatorPreserveAllTokens("ab:cd:ef", ":", 2)       = ["ab", "cd:ef"]
+     * StringUtils.splitByWholeSeparatorPreserveAllTokens("ab-!-cd-!-ef", "-!-", 5) = ["ab", "cd", "ef"]
+     * StringUtils.splitByWholeSeparatorPreserveAllTokens("ab-!-cd-!-ef", "-!-", 2) = ["ab", "cd-!-ef"]
+     * 
+ * + * @param str the String to parse, may be null + * @param separator String containing the String to be used as a delimiter, + * {@code null} splits on whitespace + * @param max the maximum number of elements to include in the returned + * array. A zero or negative value implies no limit. + * @return an array of parsed Strings, {@code null} if null String was input + * @since 2.4 + */ + public static String[] splitByWholeSeparatorPreserveAllTokens(String str, String separator, int max) { + return splitByWholeSeparatorWorker(str, separator, max, true); + } + + /** + * Performs the logic for the {@code splitByWholeSeparatorPreserveAllTokens} methods. + * + * @param str the String to parse, may be {@code null} + * @param separator String containing the String to be used as a delimiter, + * {@code null} splits on whitespace + * @param max the maximum number of elements to include in the returned + * array. A zero or negative value implies no limit. + * @param preserveAllTokens if {@code true}, adjacent separators are + * treated as empty token separators; if {@code false}, adjacent + * separators are treated as one separator. + * @return an array of parsed Strings, {@code null} if null String input + * @since 2.4 + */ + private static String[] splitByWholeSeparatorWorker( + String str, String separator, int max, boolean preserveAllTokens) { + if (str == null) { + return null; + } + + int len = str.length(); + + if (len == 0) { + return ArrayUtils.EMPTY_STRING_ARRAY; + } + + if (separator == null || EMPTY.equals(separator)) { + // Split on whitespace. + return splitWorker(str, null, max, preserveAllTokens); + } + + int separatorLength = separator.length(); + + ArrayList substrings = new ArrayList(); + int numberOfSubstrings = 0; + int beg = 0; + int end = 0; + while (end < len) { + end = str.indexOf(separator, beg); + + if (end > -1) { + if (end > beg) { + numberOfSubstrings += 1; + + if (numberOfSubstrings == max) { + end = len; + substrings.add(str.substring(beg)); + } else { + // The following is OK, because String.substring( beg, end ) excludes + // the character at the position 'end'. + substrings.add(str.substring(beg, end)); + + // Set the starting point for the next search. + // The following is equivalent to beg = end + (separatorLength - 1) + 1, + // which is the right calculation: + beg = end + separatorLength; + } + } else { + // We found a consecutive occurrence of the separator, so skip it. + if (preserveAllTokens) { + numberOfSubstrings += 1; + if (numberOfSubstrings == max) { + end = len; + substrings.add(str.substring(beg)); + } else { + substrings.add(EMPTY); + } + } + beg = end + separatorLength; + } + } else { + // String.substring( beg ) goes from 'beg' to the end of the String. + substrings.add(str.substring(beg)); + end = len; + } + } + + return substrings.toArray(new String[substrings.size()]); + } + + // ----------------------------------------------------------------------- + /** + *

Splits the provided text into an array, using whitespace as the + * separator, preserving all tokens, including empty tokens created by + * adjacent separators. This is an alternative to using StringTokenizer. + * Whitespace is defined by {@link Character#isWhitespace(char)}.

+ * + *

The separator is not included in the returned String array. + * Adjacent separators are treated as separators for empty tokens. + * For more control over the split use the StrTokenizer class.

+ * + *

A {@code null} input String returns {@code null}.

+ * + *
+     * StringUtils.splitPreserveAllTokens(null)       = null
+     * StringUtils.splitPreserveAllTokens("")         = []
+     * StringUtils.splitPreserveAllTokens("abc def")  = ["abc", "def"]
+     * StringUtils.splitPreserveAllTokens("abc  def") = ["abc", "", "def"]
+     * StringUtils.splitPreserveAllTokens(" abc ")    = ["", "abc", ""]
+     * 
+ * + * @param str the String to parse, may be {@code null} + * @return an array of parsed Strings, {@code null} if null String input + * @since 2.1 + */ + public static String[] splitPreserveAllTokens(String str) { + return splitWorker(str, null, -1, true); + } + + /** + *

Splits the provided text into an array, separator specified, + * preserving all tokens, including empty tokens created by adjacent + * separators. This is an alternative to using StringTokenizer.

+ * + *

The separator is not included in the returned String array. + * Adjacent separators are treated as separators for empty tokens. + * For more control over the split use the StrTokenizer class.

+ * + *

A {@code null} input String returns {@code null}.

+ * + *
+     * StringUtils.splitPreserveAllTokens(null, *)         = null
+     * StringUtils.splitPreserveAllTokens("", *)           = []
+     * StringUtils.splitPreserveAllTokens("a.b.c", '.')    = ["a", "b", "c"]
+     * StringUtils.splitPreserveAllTokens("a..b.c", '.')   = ["a", "", "b", "c"]
+     * StringUtils.splitPreserveAllTokens("a:b:c", '.')    = ["a:b:c"]
+     * StringUtils.splitPreserveAllTokens("a\tb\nc", null) = ["a", "b", "c"]
+     * StringUtils.splitPreserveAllTokens("a b c", ' ')    = ["a", "b", "c"]
+     * StringUtils.splitPreserveAllTokens("a b c ", ' ')   = ["a", "b", "c", ""]
+     * StringUtils.splitPreserveAllTokens("a b c  ", ' ')   = ["a", "b", "c", "", ""]
+     * StringUtils.splitPreserveAllTokens(" a b c", ' ')   = ["", a", "b", "c"]
+     * StringUtils.splitPreserveAllTokens("  a b c", ' ')  = ["", "", a", "b", "c"]
+     * StringUtils.splitPreserveAllTokens(" a b c ", ' ')  = ["", a", "b", "c", ""]
+     * 
+ * + * @param str the String to parse, may be {@code null} + * @param separatorChar the character used as the delimiter, + * {@code null} splits on whitespace + * @return an array of parsed Strings, {@code null} if null String input + * @since 2.1 + */ + public static String[] splitPreserveAllTokens(String str, char separatorChar) { + return splitWorker(str, separatorChar, true); + } + + /** + * Performs the logic for the {@code split} and + * {@code splitPreserveAllTokens} methods that do not return a + * maximum array length. + * + * @param str the String to parse, may be {@code null} + * @param separatorChar the separate character + * @param preserveAllTokens if {@code true}, adjacent separators are + * treated as empty token separators; if {@code false}, adjacent + * separators are treated as one separator. + * @return an array of parsed Strings, {@code null} if null String input + */ + private static String[] splitWorker(String str, char separatorChar, boolean preserveAllTokens) { + // Performance tuned for 2.0 (JDK1.4) + + if (str == null) { + return null; + } + int len = str.length(); + if (len == 0) { + return ArrayUtils.EMPTY_STRING_ARRAY; + } + List list = new ArrayList(); + int i = 0, start = 0; + boolean match = false; + boolean lastMatch = false; + while (i < len) { + if (str.charAt(i) == separatorChar) { + if (match || preserveAllTokens) { + list.add(str.substring(start, i)); + match = false; + lastMatch = true; + } + start = ++i; + continue; + } + lastMatch = false; + match = true; + i++; + } + if (match || preserveAllTokens && lastMatch) { + list.add(str.substring(start, i)); + } + return list.toArray(new String[list.size()]); + } + + /** + *

Splits the provided text into an array, separators specified, + * preserving all tokens, including empty tokens created by adjacent + * separators. This is an alternative to using StringTokenizer.

+ * + *

The separator is not included in the returned String array. + * Adjacent separators are treated as separators for empty tokens. + * For more control over the split use the StrTokenizer class.

+ * + *

A {@code null} input String returns {@code null}. + * A {@code null} separatorChars splits on whitespace.

+ * + *
+     * StringUtils.splitPreserveAllTokens(null, *)           = null
+     * StringUtils.splitPreserveAllTokens("", *)             = []
+     * StringUtils.splitPreserveAllTokens("abc def", null)   = ["abc", "def"]
+     * StringUtils.splitPreserveAllTokens("abc def", " ")    = ["abc", "def"]
+     * StringUtils.splitPreserveAllTokens("abc  def", " ")   = ["abc", "", def"]
+     * StringUtils.splitPreserveAllTokens("ab:cd:ef", ":")   = ["ab", "cd", "ef"]
+     * StringUtils.splitPreserveAllTokens("ab:cd:ef:", ":")  = ["ab", "cd", "ef", ""]
+     * StringUtils.splitPreserveAllTokens("ab:cd:ef::", ":") = ["ab", "cd", "ef", "", ""]
+     * StringUtils.splitPreserveAllTokens("ab::cd:ef", ":")  = ["ab", "", cd", "ef"]
+     * StringUtils.splitPreserveAllTokens(":cd:ef", ":")     = ["", cd", "ef"]
+     * StringUtils.splitPreserveAllTokens("::cd:ef", ":")    = ["", "", cd", "ef"]
+     * StringUtils.splitPreserveAllTokens(":cd:ef:", ":")    = ["", cd", "ef", ""]
+     * 
+ * + * @param str the String to parse, may be {@code null} + * @param separatorChars the characters used as the delimiters, + * {@code null} splits on whitespace + * @return an array of parsed Strings, {@code null} if null String input + * @since 2.1 + */ + public static String[] splitPreserveAllTokens(String str, String separatorChars) { + return splitWorker(str, separatorChars, -1, true); + } + + /** + *

Splits the provided text into an array with a maximum length, + * separators specified, preserving all tokens, including empty tokens + * created by adjacent separators.

+ * + *

The separator is not included in the returned String array. + * Adjacent separators are treated as separators for empty tokens. + * Adjacent separators are treated as one separator.

+ * + *

A {@code null} input String returns {@code null}. + * A {@code null} separatorChars splits on whitespace.

+ * + *

If more than {@code max} delimited substrings are found, the last + * returned string includes all characters after the first {@code max - 1} + * returned strings (including separator characters).

+ * + *
+     * StringUtils.splitPreserveAllTokens(null, *, *)            = null
+     * StringUtils.splitPreserveAllTokens("", *, *)              = []
+     * StringUtils.splitPreserveAllTokens("ab de fg", null, 0)   = ["ab", "cd", "ef"]
+     * StringUtils.splitPreserveAllTokens("ab   de fg", null, 0) = ["ab", "cd", "ef"]
+     * StringUtils.splitPreserveAllTokens("ab:cd:ef", ":", 0)    = ["ab", "cd", "ef"]
+     * StringUtils.splitPreserveAllTokens("ab:cd:ef", ":", 2)    = ["ab", "cd:ef"]
+     * StringUtils.splitPreserveAllTokens("ab   de fg", null, 2) = ["ab", "  de fg"]
+     * StringUtils.splitPreserveAllTokens("ab   de fg", null, 3) = ["ab", "", " de fg"]
+     * StringUtils.splitPreserveAllTokens("ab   de fg", null, 4) = ["ab", "", "", "de fg"]
+     * 
+ * + * @param str the String to parse, may be {@code null} + * @param separatorChars the characters used as the delimiters, + * {@code null} splits on whitespace + * @param max the maximum number of elements to include in the + * array. A zero or negative value implies no limit + * @return an array of parsed Strings, {@code null} if null String input + * @since 2.1 + */ + public static String[] splitPreserveAllTokens(String str, String separatorChars, int max) { + return splitWorker(str, separatorChars, max, true); + } + + /** + * Performs the logic for the {@code split} and + * {@code splitPreserveAllTokens} methods that return a maximum array + * length. + * + * @param str the String to parse, may be {@code null} + * @param separatorChars the separate character + * @param max the maximum number of elements to include in the + * array. A zero or negative value implies no limit. + * @param preserveAllTokens if {@code true}, adjacent separators are + * treated as empty token separators; if {@code false}, adjacent + * separators are treated as one separator. + * @return an array of parsed Strings, {@code null} if null String input + */ + private static String[] splitWorker(String str, String separatorChars, int max, boolean preserveAllTokens) { + // Performance tuned for 2.0 (JDK1.4) + // Direct code is quicker than StringTokenizer. + // Also, StringTokenizer uses isSpace() not isWhitespace() + + if (str == null) { + return null; + } + int len = str.length(); + if (len == 0) { + return ArrayUtils.EMPTY_STRING_ARRAY; + } + List list = new ArrayList(); + int sizePlus1 = 1; + int i = 0, start = 0; + boolean match = false; + boolean lastMatch = false; + if (separatorChars == null) { + // Null separator means use whitespace + while (i < len) { + if (Character.isWhitespace(str.charAt(i))) { + if (match || preserveAllTokens) { + lastMatch = true; + if (sizePlus1++ == max) { + i = len; + lastMatch = false; + } + list.add(str.substring(start, i)); + match = false; + } + start = ++i; + continue; + } + lastMatch = false; + match = true; + i++; + } + } else if (separatorChars.length() == 1) { + // Optimise 1 character case + char sep = separatorChars.charAt(0); + while (i < len) { + if (str.charAt(i) == sep) { + if (match || preserveAllTokens) { + lastMatch = true; + if (sizePlus1++ == max) { + i = len; + lastMatch = false; + } + list.add(str.substring(start, i)); + match = false; + } + start = ++i; + continue; + } + lastMatch = false; + match = true; + i++; + } + } else { + // standard case + while (i < len) { + if (separatorChars.indexOf(str.charAt(i)) >= 0) { + if (match || preserveAllTokens) { + lastMatch = true; + if (sizePlus1++ == max) { + i = len; + lastMatch = false; + } + list.add(str.substring(start, i)); + match = false; + } + start = ++i; + continue; + } + lastMatch = false; + match = true; + i++; + } + } + if (match || preserveAllTokens && lastMatch) { + list.add(str.substring(start, i)); + } + return list.toArray(new String[list.size()]); + } + + /** + *

Splits a String by Character type as returned by + * {@code java.lang.Character.getType(char)}. Groups of contiguous + * characters of the same type are returned as complete tokens. + *

+     * StringUtils.splitByCharacterType(null)         = null
+     * StringUtils.splitByCharacterType("")           = []
+     * StringUtils.splitByCharacterType("ab de fg")   = ["ab", " ", "de", " ", "fg"]
+     * StringUtils.splitByCharacterType("ab   de fg") = ["ab", "   ", "de", " ", "fg"]
+     * StringUtils.splitByCharacterType("ab:cd:ef")   = ["ab", ":", "cd", ":", "ef"]
+     * StringUtils.splitByCharacterType("number5")    = ["number", "5"]
+     * StringUtils.splitByCharacterType("fooBar")     = ["foo", "B", "ar"]
+     * StringUtils.splitByCharacterType("foo200Bar")  = ["foo", "200", "B", "ar"]
+     * StringUtils.splitByCharacterType("ASFRules")   = ["ASFR", "ules"]
+     * 
+ * @param str the String to split, may be {@code null} + * @return an array of parsed Strings, {@code null} if null String input + * @since 2.4 + */ + public static String[] splitByCharacterType(String str) { + return splitByCharacterType(str, false); + } + + /** + *

Splits a String by Character type as returned by + * {@code java.lang.Character.getType(char)}. Groups of contiguous + * characters of the same type are returned as complete tokens, with the + * following exception: the character of type + * {@code Character.UPPERCASE_LETTER}, if any, immediately + * preceding a token of type {@code Character.LOWERCASE_LETTER} + * will belong to the following token rather than to the preceding, if any, + * {@code Character.UPPERCASE_LETTER} token. + *

+     * StringUtils.splitByCharacterTypeCamelCase(null)         = null
+     * StringUtils.splitByCharacterTypeCamelCase("")           = []
+     * StringUtils.splitByCharacterTypeCamelCase("ab de fg")   = ["ab", " ", "de", " ", "fg"]
+     * StringUtils.splitByCharacterTypeCamelCase("ab   de fg") = ["ab", "   ", "de", " ", "fg"]
+     * StringUtils.splitByCharacterTypeCamelCase("ab:cd:ef")   = ["ab", ":", "cd", ":", "ef"]
+     * StringUtils.splitByCharacterTypeCamelCase("number5")    = ["number", "5"]
+     * StringUtils.splitByCharacterTypeCamelCase("fooBar")     = ["foo", "Bar"]
+     * StringUtils.splitByCharacterTypeCamelCase("foo200Bar")  = ["foo", "200", "Bar"]
+     * StringUtils.splitByCharacterTypeCamelCase("ASFRules")   = ["ASF", "Rules"]
+     * 
+ * @param str the String to split, may be {@code null} + * @return an array of parsed Strings, {@code null} if null String input + * @since 2.4 + */ + public static String[] splitByCharacterTypeCamelCase(String str) { + return splitByCharacterType(str, true); + } + + /** + *

Splits a String by Character type as returned by + * {@code java.lang.Character.getType(char)}. Groups of contiguous + * characters of the same type are returned as complete tokens, with the + * following exception: if {@code camelCase} is {@code true}, + * the character of type {@code Character.UPPERCASE_LETTER}, if any, + * immediately preceding a token of type {@code Character.LOWERCASE_LETTER} + * will belong to the following token rather than to the preceding, if any, + * {@code Character.UPPERCASE_LETTER} token. + * @param str the String to split, may be {@code null} + * @param camelCase whether to use so-called "camel-case" for letter types + * @return an array of parsed Strings, {@code null} if null String input + * @since 2.4 + */ + private static String[] splitByCharacterType(String str, boolean camelCase) { + if (str == null) { + return null; + } + if (str.length() == 0) { + return ArrayUtils.EMPTY_STRING_ARRAY; + } + char[] c = str.toCharArray(); + List list = new ArrayList(); + int tokenStart = 0; + int currentType = Character.getType(c[tokenStart]); + for (int pos = tokenStart + 1; pos < c.length; pos++) { + int type = Character.getType(c[pos]); + if (type == currentType) { + continue; + } + if (camelCase && type == Character.LOWERCASE_LETTER && currentType == Character.UPPERCASE_LETTER) { + int newTokenStart = pos - 1; + if (newTokenStart != tokenStart) { + list.add(new String(c, tokenStart, newTokenStart - tokenStart)); + tokenStart = newTokenStart; + } + } else { + list.add(new String(c, tokenStart, pos - tokenStart)); + tokenStart = pos; + } + currentType = type; + } + list.add(new String(c, tokenStart, c.length - tokenStart)); + return list.toArray(new String[list.size()]); + } + + // Joining + //----------------------------------------------------------------------- + /** + *

Joins the elements of the provided array into a single String + * containing the provided list of elements.

+ * + *

No separator is added to the joined String. + * Null objects or empty strings within the array are represented by + * empty strings.

+ * + *
+     * StringUtils.join(null)            = null
+     * StringUtils.join([])              = ""
+     * StringUtils.join([null])          = ""
+     * StringUtils.join(["a", "b", "c"]) = "abc"
+     * StringUtils.join([null, "", "a"]) = "a"
+     * 
+ * + * @param the specific type of values to join together + * @param elements the values to join together, may be null + * @return the joined String, {@code null} if null array input + * @since 2.0 + * @since 3.0 Changed signature to use varargs + */ + public static String join(T... elements) { + return join(elements, null); + } + + /** + *

Joins the elements of the provided array into a single String + * containing the provided list of elements.

+ * + *

No delimiter is added before or after the list. + * Null objects or empty strings within the array are represented by + * empty strings.

+ * + *
+     * StringUtils.join(null, *)               = null
+     * StringUtils.join([], *)                 = ""
+     * StringUtils.join([null], *)             = ""
+     * StringUtils.join(["a", "b", "c"], ';')  = "a;b;c"
+     * StringUtils.join(["a", "b", "c"], null) = "abc"
+     * StringUtils.join([null, "", "a"], ';')  = ";;a"
+     * 
+ * + * @param array the array of values to join together, may be null + * @param separator the separator character to use + * @return the joined String, {@code null} if null array input + * @since 2.0 + */ + public static String join(Object[] array, char separator) { + if (array == null) { + return null; + } + + return join(array, separator, 0, array.length); + } + + /** + *

Joins the elements of the provided array into a single String + * containing the provided list of elements.

+ * + *

No delimiter is added before or after the list. + * Null objects or empty strings within the array are represented by + * empty strings.

+ * + *
+     * StringUtils.join(null, *)               = null
+     * StringUtils.join([], *)                 = ""
+     * StringUtils.join([null], *)             = ""
+     * StringUtils.join(["a", "b", "c"], ';')  = "a;b;c"
+     * StringUtils.join(["a", "b", "c"], null) = "abc"
+     * StringUtils.join([null, "", "a"], ';')  = ";;a"
+     * 
+ * + * @param array the array of values to join together, may be null + * @param separator the separator character to use + * @param startIndex the first index to start joining from. It is + * an error to pass in an end index past the end of the array + * @param endIndex the index to stop joining from (exclusive). It is + * an error to pass in an end index past the end of the array + * @return the joined String, {@code null} if null array input + * @since 2.0 + */ + public static String join(Object[] array, char separator, int startIndex, int endIndex) { + if (array == null) { + return null; + } + int noOfItems = endIndex - startIndex; + if (noOfItems <= 0) { + return EMPTY; + } + + StringBuilder buf = new StringBuilder(noOfItems * 16); + + for (int i = startIndex; i < endIndex; i++) { + if (i > startIndex) { + buf.append(separator); + } + if (array[i] != null) { + buf.append(array[i]); + } + } + return buf.toString(); + } + + /** + *

Joins the elements of the provided array into a single String + * containing the provided list of elements.

+ * + *

No delimiter is added before or after the list. + * A {@code null} separator is the same as an empty String (""). + * Null objects or empty strings within the array are represented by + * empty strings.

+ * + *
+     * StringUtils.join(null, *)                = null
+     * StringUtils.join([], *)                  = ""
+     * StringUtils.join([null], *)              = ""
+     * StringUtils.join(["a", "b", "c"], "--")  = "a--b--c"
+     * StringUtils.join(["a", "b", "c"], null)  = "abc"
+     * StringUtils.join(["a", "b", "c"], "")    = "abc"
+     * StringUtils.join([null, "", "a"], ',')   = ",,a"
+     * 
+ * + * @param array the array of values to join together, may be null + * @param separator the separator character to use, null treated as "" + * @return the joined String, {@code null} if null array input + */ + public static String join(Object[] array, String separator) { + if (array == null) { + return null; + } + return join(array, separator, 0, array.length); + } + + /** + *

Joins the elements of the provided array into a single String + * containing the provided list of elements.

+ * + *

No delimiter is added before or after the list. + * A {@code null} separator is the same as an empty String (""). + * Null objects or empty strings within the array are represented by + * empty strings.

+ * + *
+     * StringUtils.join(null, *)                = null
+     * StringUtils.join([], *)                  = ""
+     * StringUtils.join([null], *)              = ""
+     * StringUtils.join(["a", "b", "c"], "--")  = "a--b--c"
+     * StringUtils.join(["a", "b", "c"], null)  = "abc"
+     * StringUtils.join(["a", "b", "c"], "")    = "abc"
+     * StringUtils.join([null, "", "a"], ',')   = ",,a"
+     * 
+ * + * @param array the array of values to join together, may be null + * @param separator the separator character to use, null treated as "" + * @param startIndex the first index to start joining from. It is + * an error to pass in an end index past the end of the array + * @param endIndex the index to stop joining from (exclusive). It is + * an error to pass in an end index past the end of the array + * @return the joined String, {@code null} if null array input + */ + public static String join(Object[] array, String separator, int startIndex, int endIndex) { + if (array == null) { + return null; + } + if (separator == null) { + separator = EMPTY; + } + + // endIndex - startIndex > 0: Len = NofStrings *(len(firstString) + len(separator)) + // (Assuming that all Strings are roughly equally long) + int noOfItems = endIndex - startIndex; + if (noOfItems <= 0) { + return EMPTY; + } + + StringBuilder buf = new StringBuilder(noOfItems * 16); + + for (int i = startIndex; i < endIndex; i++) { + if (i > startIndex) { + buf.append(separator); + } + if (array[i] != null) { + buf.append(array[i]); + } + } + return buf.toString(); + } + + /** + *

Joins the elements of the provided {@code Iterator} into + * a single String containing the provided elements.

+ * + *

No delimiter is added before or after the list. Null objects or empty + * strings within the iteration are represented by empty strings.

+ * + *

See the examples here: {@link #join(Object[],char)}.

+ * + * @param iterator the {@code Iterator} of values to join together, may be null + * @param separator the separator character to use + * @return the joined String, {@code null} if null iterator input + * @since 2.0 + */ + public static String join(Iterator iterator, char separator) { + + // handle null, zero and one elements before building a buffer + if (iterator == null) { + return null; + } + if (!iterator.hasNext()) { + return EMPTY; + } + Object first = iterator.next(); + if (!iterator.hasNext()) { + return ObjectUtils.toString(first); + } + + // two or more elements + StringBuilder buf = new StringBuilder(256); // Java default is 16, probably too small + if (first != null) { + buf.append(first); + } + + while (iterator.hasNext()) { + buf.append(separator); + Object obj = iterator.next(); + if (obj != null) { + buf.append(obj); + } + } + + return buf.toString(); + } + + /** + *

Joins the elements of the provided {@code Iterator} into + * a single String containing the provided elements.

+ * + *

No delimiter is added before or after the list. + * A {@code null} separator is the same as an empty String ("").

+ * + *

See the examples here: {@link #join(Object[],String)}.

+ * + * @param iterator the {@code Iterator} of values to join together, may be null + * @param separator the separator character to use, null treated as "" + * @return the joined String, {@code null} if null iterator input + */ + public static String join(Iterator iterator, String separator) { + + // handle null, zero and one elements before building a buffer + if (iterator == null) { + return null; + } + if (!iterator.hasNext()) { + return EMPTY; + } + Object first = iterator.next(); + if (!iterator.hasNext()) { + return ObjectUtils.toString(first); + } + + // two or more elements + StringBuilder buf = new StringBuilder(256); // Java default is 16, probably too small + if (first != null) { + buf.append(first); + } + + while (iterator.hasNext()) { + if (separator != null) { + buf.append(separator); + } + Object obj = iterator.next(); + if (obj != null) { + buf.append(obj); + } + } + return buf.toString(); + } + + /** + *

Joins the elements of the provided {@code Iterable} into + * a single String containing the provided elements.

+ * + *

No delimiter is added before or after the list. Null objects or empty + * strings within the iteration are represented by empty strings.

+ * + *

See the examples here: {@link #join(Object[],char)}.

+ * + * @param iterable the {@code Iterable} providing the values to join together, may be null + * @param separator the separator character to use + * @return the joined String, {@code null} if null iterator input + * @since 2.3 + */ + public static String join(Iterable iterable, char separator) { + if (iterable == null) { + return null; + } + return join(iterable.iterator(), separator); + } + + /** + *

Joins the elements of the provided {@code Iterable} into + * a single String containing the provided elements.

+ * + *

No delimiter is added before or after the list. + * A {@code null} separator is the same as an empty String ("").

+ * + *

See the examples here: {@link #join(Object[],String)}.

+ * + * @param iterable the {@code Iterable} providing the values to join together, may be null + * @param separator the separator character to use, null treated as "" + * @return the joined String, {@code null} if null iterator input + * @since 2.3 + */ + public static String join(Iterable iterable, String separator) { + if (iterable == null) { + return null; + } + return join(iterable.iterator(), separator); + } + + // Delete + //----------------------------------------------------------------------- + /** + *

Deletes all whitespaces from a String as defined by + * {@link Character#isWhitespace(char)}.

+ * + *
+     * StringUtils.deleteWhitespace(null)         = null
+     * StringUtils.deleteWhitespace("")           = ""
+     * StringUtils.deleteWhitespace("abc")        = "abc"
+     * StringUtils.deleteWhitespace("   ab  c  ") = "abc"
+     * 
+ * + * @param str the String to delete whitespace from, may be null + * @return the String without whitespaces, {@code null} if null String input + */ + public static String deleteWhitespace(String str) { + if (isEmpty(str)) { + return str; + } + int sz = str.length(); + char[] chs = new char[sz]; + int count = 0; + for (int i = 0; i < sz; i++) { + if (!Character.isWhitespace(str.charAt(i))) { + chs[count++] = str.charAt(i); + } + } + if (count == sz) { + return str; + } + return new String(chs, 0, count); + } + + // Remove + //----------------------------------------------------------------------- + /** + *

Removes a substring only if it is at the beginning of a source string, + * otherwise returns the source string.

+ * + *

A {@code null} source string will return {@code null}. + * An empty ("") source string will return the empty string. + * A {@code null} search string will return the source string.

+ * + *
+     * StringUtils.removeStart(null, *)      = null
+     * StringUtils.removeStart("", *)        = ""
+     * StringUtils.removeStart(*, null)      = *
+     * StringUtils.removeStart("www.domain.com", "www.")   = "domain.com"
+     * StringUtils.removeStart("domain.com", "www.")       = "domain.com"
+     * StringUtils.removeStart("www.domain.com", "domain") = "www.domain.com"
+     * StringUtils.removeStart("abc", "")    = "abc"
+     * 
+ * + * @param str the source String to search, may be null + * @param remove the String to search for and remove, may be null + * @return the substring with the string removed if found, + * {@code null} if null String input + * @since 2.1 + */ + public static String removeStart(String str, String remove) { + if (isEmpty(str) || isEmpty(remove)) { + return str; + } + if (str.startsWith(remove)){ + return str.substring(remove.length()); + } + return str; + } + + /** + *

Case insensitive removal of a substring if it is at the beginning of a source string, + * otherwise returns the source string.

+ * + *

A {@code null} source string will return {@code null}. + * An empty ("") source string will return the empty string. + * A {@code null} search string will return the source string.

+ * + *
+     * StringUtils.removeStartIgnoreCase(null, *)      = null
+     * StringUtils.removeStartIgnoreCase("", *)        = ""
+     * StringUtils.removeStartIgnoreCase(*, null)      = *
+     * StringUtils.removeStartIgnoreCase("www.domain.com", "www.")   = "domain.com"
+     * StringUtils.removeStartIgnoreCase("www.domain.com", "WWW.")   = "domain.com"
+     * StringUtils.removeStartIgnoreCase("domain.com", "www.")       = "domain.com"
+     * StringUtils.removeStartIgnoreCase("www.domain.com", "domain") = "www.domain.com"
+     * StringUtils.removeStartIgnoreCase("abc", "")    = "abc"
+     * 
+ * + * @param str the source String to search, may be null + * @param remove the String to search for (case insensitive) and remove, may be null + * @return the substring with the string removed if found, + * {@code null} if null String input + * @since 2.4 + */ + public static String removeStartIgnoreCase(String str, String remove) { + if (isEmpty(str) || isEmpty(remove)) { + return str; + } + if (startsWithIgnoreCase(str, remove)) { + return str.substring(remove.length()); + } + return str; + } + + /** + *

Removes a substring only if it is at the end of a source string, + * otherwise returns the source string.

+ * + *

A {@code null} source string will return {@code null}. + * An empty ("") source string will return the empty string. + * A {@code null} search string will return the source string.

+ * + *
+     * StringUtils.removeEnd(null, *)      = null
+     * StringUtils.removeEnd("", *)        = ""
+     * StringUtils.removeEnd(*, null)      = *
+     * StringUtils.removeEnd("www.domain.com", ".com.")  = "www.domain.com"
+     * StringUtils.removeEnd("www.domain.com", ".com")   = "www.domain"
+     * StringUtils.removeEnd("www.domain.com", "domain") = "www.domain.com"
+     * StringUtils.removeEnd("abc", "")    = "abc"
+     * 
+ * + * @param str the source String to search, may be null + * @param remove the String to search for and remove, may be null + * @return the substring with the string removed if found, + * {@code null} if null String input + * @since 2.1 + */ + public static String removeEnd(String str, String remove) { + if (isEmpty(str) || isEmpty(remove)) { + return str; + } + if (str.endsWith(remove)) { + return str.substring(0, str.length() - remove.length()); + } + return str; + } + + /** + *

Case insensitive removal of a substring if it is at the end of a source string, + * otherwise returns the source string.

+ * + *

A {@code null} source string will return {@code null}. + * An empty ("") source string will return the empty string. + * A {@code null} search string will return the source string.

+ * + *
+     * StringUtils.removeEndIgnoreCase(null, *)      = null
+     * StringUtils.removeEndIgnoreCase("", *)        = ""
+     * StringUtils.removeEndIgnoreCase(*, null)      = *
+     * StringUtils.removeEndIgnoreCase("www.domain.com", ".com.")  = "www.domain.com"
+     * StringUtils.removeEndIgnoreCase("www.domain.com", ".com")   = "www.domain"
+     * StringUtils.removeEndIgnoreCase("www.domain.com", "domain") = "www.domain.com"
+     * StringUtils.removeEndIgnoreCase("abc", "")    = "abc"
+     * StringUtils.removeEndIgnoreCase("www.domain.com", ".COM") = "www.domain")
+     * StringUtils.removeEndIgnoreCase("www.domain.COM", ".com") = "www.domain")
+     * 
+ * + * @param str the source String to search, may be null + * @param remove the String to search for (case insensitive) and remove, may be null + * @return the substring with the string removed if found, + * {@code null} if null String input + * @since 2.4 + */ + public static String removeEndIgnoreCase(String str, String remove) { + if (isEmpty(str) || isEmpty(remove)) { + return str; + } + if (endsWithIgnoreCase(str, remove)) { + return str.substring(0, str.length() - remove.length()); + } + return str; + } + + /** + *

Removes all occurrences of a substring from within the source string.

+ * + *

A {@code null} source string will return {@code null}. + * An empty ("") source string will return the empty string. + * A {@code null} remove string will return the source string. + * An empty ("") remove string will return the source string.

+ * + *
+     * StringUtils.remove(null, *)        = null
+     * StringUtils.remove("", *)          = ""
+     * StringUtils.remove(*, null)        = *
+     * StringUtils.remove(*, "")          = *
+     * StringUtils.remove("queued", "ue") = "qd"
+     * StringUtils.remove("queued", "zz") = "queued"
+     * 
+ * + * @param str the source String to search, may be null + * @param remove the String to search for and remove, may be null + * @return the substring with the string removed if found, + * {@code null} if null String input + * @since 2.1 + */ + public static String remove(String str, String remove) { + if (isEmpty(str) || isEmpty(remove)) { + return str; + } + return replace(str, remove, EMPTY, -1); + } + + /** + *

Removes all occurrences of a character from within the source string.

+ * + *

A {@code null} source string will return {@code null}. + * An empty ("") source string will return the empty string.

+ * + *
+     * StringUtils.remove(null, *)       = null
+     * StringUtils.remove("", *)         = ""
+     * StringUtils.remove("queued", 'u') = "qeed"
+     * StringUtils.remove("queued", 'z') = "queued"
+     * 
+ * + * @param str the source String to search, may be null + * @param remove the char to search for and remove, may be null + * @return the substring with the char removed if found, + * {@code null} if null String input + * @since 2.1 + */ + public static String remove(String str, char remove) { + if (isEmpty(str) || str.indexOf(remove) == INDEX_NOT_FOUND) { + return str; + } + char[] chars = str.toCharArray(); + int pos = 0; + for (int i = 0; i < chars.length; i++) { + if (chars[i] != remove) { + chars[pos++] = chars[i]; + } + } + return new String(chars, 0, pos); + } + + // Replacing + //----------------------------------------------------------------------- + /** + *

Replaces a String with another String inside a larger String, once.

+ * + *

A {@code null} reference passed to this method is a no-op.

+ * + *
+     * StringUtils.replaceOnce(null, *, *)        = null
+     * StringUtils.replaceOnce("", *, *)          = ""
+     * StringUtils.replaceOnce("any", null, *)    = "any"
+     * StringUtils.replaceOnce("any", *, null)    = "any"
+     * StringUtils.replaceOnce("any", "", *)      = "any"
+     * StringUtils.replaceOnce("aba", "a", null)  = "aba"
+     * StringUtils.replaceOnce("aba", "a", "")    = "ba"
+     * StringUtils.replaceOnce("aba", "a", "z")   = "zba"
+     * 
+ * + * @see #replace(String text, String searchString, String replacement, int max) + * @param text text to search and replace in, may be null + * @param searchString the String to search for, may be null + * @param replacement the String to replace with, may be null + * @return the text with any replacements processed, + * {@code null} if null String input + */ + public static String replaceOnce(String text, String searchString, String replacement) { + return replace(text, searchString, replacement, 1); + } + + /** + *

Replaces all occurrences of a String within another String.

+ * + *

A {@code null} reference passed to this method is a no-op.

+ * + *
+     * StringUtils.replace(null, *, *)        = null
+     * StringUtils.replace("", *, *)          = ""
+     * StringUtils.replace("any", null, *)    = "any"
+     * StringUtils.replace("any", *, null)    = "any"
+     * StringUtils.replace("any", "", *)      = "any"
+     * StringUtils.replace("aba", "a", null)  = "aba"
+     * StringUtils.replace("aba", "a", "")    = "b"
+     * StringUtils.replace("aba", "a", "z")   = "zbz"
+     * 
+ * + * @see #replace(String text, String searchString, String replacement, int max) + * @param text text to search and replace in, may be null + * @param searchString the String to search for, may be null + * @param replacement the String to replace it with, may be null + * @return the text with any replacements processed, + * {@code null} if null String input + */ + public static String replace(String text, String searchString, String replacement) { + return replace(text, searchString, replacement, -1); + } + + /** + *

Replaces a String with another String inside a larger String, + * for the first {@code max} values of the search String.

+ * + *

A {@code null} reference passed to this method is a no-op.

+ * + *
+     * StringUtils.replace(null, *, *, *)         = null
+     * StringUtils.replace("", *, *, *)           = ""
+     * StringUtils.replace("any", null, *, *)     = "any"
+     * StringUtils.replace("any", *, null, *)     = "any"
+     * StringUtils.replace("any", "", *, *)       = "any"
+     * StringUtils.replace("any", *, *, 0)        = "any"
+     * StringUtils.replace("abaa", "a", null, -1) = "abaa"
+     * StringUtils.replace("abaa", "a", "", -1)   = "b"
+     * StringUtils.replace("abaa", "a", "z", 0)   = "abaa"
+     * StringUtils.replace("abaa", "a", "z", 1)   = "zbaa"
+     * StringUtils.replace("abaa", "a", "z", 2)   = "zbza"
+     * StringUtils.replace("abaa", "a", "z", -1)  = "zbzz"
+     * 
+ * + * @param text text to search and replace in, may be null + * @param searchString the String to search for, may be null + * @param replacement the String to replace it with, may be null + * @param max maximum number of values to replace, or {@code -1} if no maximum + * @return the text with any replacements processed, + * {@code null} if null String input + */ + public static String replace(String text, String searchString, String replacement, int max) { + if (isEmpty(text) || isEmpty(searchString) || replacement == null || max == 0) { + return text; + } + int start = 0; + int end = text.indexOf(searchString, start); + if (end == INDEX_NOT_FOUND) { + return text; + } + int replLength = searchString.length(); + int increase = replacement.length() - replLength; + increase = increase < 0 ? 0 : increase; + increase *= max < 0 ? 16 : max > 64 ? 64 : max; + StringBuilder buf = new StringBuilder(text.length() + increase); + while (end != INDEX_NOT_FOUND) { + buf.append(text.substring(start, end)).append(replacement); + start = end + replLength; + if (--max == 0) { + break; + } + end = text.indexOf(searchString, start); + } + buf.append(text.substring(start)); + return buf.toString(); + } + + /** + *

+ * Replaces all occurrences of Strings within another String. + *

+ * + *

+ * A {@code null} reference passed to this method is a no-op, or if + * any "search string" or "string to replace" is null, that replace will be + * ignored. This will not repeat. For repeating replaces, call the + * overloaded method. + *

+ * + *
+     *  StringUtils.replaceEach(null, *, *)        = null
+     *  StringUtils.replaceEach("", *, *)          = ""
+     *  StringUtils.replaceEach("aba", null, null) = "aba"
+     *  StringUtils.replaceEach("aba", new String[0], null) = "aba"
+     *  StringUtils.replaceEach("aba", null, new String[0]) = "aba"
+     *  StringUtils.replaceEach("aba", new String[]{"a"}, null)  = "aba"
+     *  StringUtils.replaceEach("aba", new String[]{"a"}, new String[]{""})  = "b"
+     *  StringUtils.replaceEach("aba", new String[]{null}, new String[]{"a"})  = "aba"
+     *  StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"w", "t"})  = "wcte"
+     *  (example of how it does not repeat)
+     *  StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"})  = "dcte"
+     * 
+ * + * @param text + * text to search and replace in, no-op if null + * @param searchList + * the Strings to search for, no-op if null + * @param replacementList + * the Strings to replace them with, no-op if null + * @return the text with any replacements processed, {@code null} if + * null String input + * @throws IllegalArgumentException + * if the lengths of the arrays are not the same (null is ok, + * and/or size 0) + * @since 2.4 + */ + public static String replaceEach(String text, String[] searchList, String[] replacementList) { + return replaceEach(text, searchList, replacementList, false, 0); + } + + /** + *

+ * Replaces all occurrences of Strings within another String. + *

+ * + *

+ * A {@code null} reference passed to this method is a no-op, or if + * any "search string" or "string to replace" is null, that replace will be + * ignored. + *

+ * + *
+     *  StringUtils.replaceEach(null, *, *, *) = null
+     *  StringUtils.replaceEach("", *, *, *) = ""
+     *  StringUtils.replaceEach("aba", null, null, *) = "aba"
+     *  StringUtils.replaceEach("aba", new String[0], null, *) = "aba"
+     *  StringUtils.replaceEach("aba", null, new String[0], *) = "aba"
+     *  StringUtils.replaceEach("aba", new String[]{"a"}, null, *) = "aba"
+     *  StringUtils.replaceEach("aba", new String[]{"a"}, new String[]{""}, *) = "b"
+     *  StringUtils.replaceEach("aba", new String[]{null}, new String[]{"a"}, *) = "aba"
+     *  StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"w", "t"}, *) = "wcte"
+     *  (example of how it repeats)
+     *  StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"}, false) = "dcte"
+     *  StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"}, true) = "tcte"
+     *  StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "ab"}, true) = IllegalStateException
+     *  StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "ab"}, false) = "dcabe"
+     * 
+ * + * @param text + * text to search and replace in, no-op if null + * @param searchList + * the Strings to search for, no-op if null + * @param replacementList + * the Strings to replace them with, no-op if null + * @return the text with any replacements processed, {@code null} if + * null String input + * @throws IllegalStateException + * if the search is repeating and there is an endless loop due + * to outputs of one being inputs to another + * @throws IllegalArgumentException + * if the lengths of the arrays are not the same (null is ok, + * and/or size 0) + * @since 2.4 + */ + public static String replaceEachRepeatedly(String text, String[] searchList, String[] replacementList) { + // timeToLive should be 0 if not used or nothing to replace, else it's + // the length of the replace array + int timeToLive = searchList == null ? 0 : searchList.length; + return replaceEach(text, searchList, replacementList, true, timeToLive); + } + + /** + *

+ * Replaces all occurrences of Strings within another String. + *

+ * + *

+ * A {@code null} reference passed to this method is a no-op, or if + * any "search string" or "string to replace" is null, that replace will be + * ignored. + *

+ * + *
+     *  StringUtils.replaceEach(null, *, *, *) = null
+     *  StringUtils.replaceEach("", *, *, *) = ""
+     *  StringUtils.replaceEach("aba", null, null, *) = "aba"
+     *  StringUtils.replaceEach("aba", new String[0], null, *) = "aba"
+     *  StringUtils.replaceEach("aba", null, new String[0], *) = "aba"
+     *  StringUtils.replaceEach("aba", new String[]{"a"}, null, *) = "aba"
+     *  StringUtils.replaceEach("aba", new String[]{"a"}, new String[]{""}, *) = "b"
+     *  StringUtils.replaceEach("aba", new String[]{null}, new String[]{"a"}, *) = "aba"
+     *  StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"w", "t"}, *) = "wcte"
+     *  (example of how it repeats)
+     *  StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"}, false) = "dcte"
+     *  StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"}, true) = "tcte"
+     *  StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "ab"}, *) = IllegalStateException
+     * 
+ * + * @param text + * text to search and replace in, no-op if null + * @param searchList + * the Strings to search for, no-op if null + * @param replacementList + * the Strings to replace them with, no-op if null + * @param repeat if true, then replace repeatedly + * until there are no more possible replacements or timeToLive < 0 + * @param timeToLive + * if less than 0 then there is a circular reference and endless + * loop + * @return the text with any replacements processed, {@code null} if + * null String input + * @throws IllegalStateException + * if the search is repeating and there is an endless loop due + * to outputs of one being inputs to another + * @throws IllegalArgumentException + * if the lengths of the arrays are not the same (null is ok, + * and/or size 0) + * @since 2.4 + */ + private static String replaceEach( + String text, String[] searchList, String[] replacementList, boolean repeat, int timeToLive) { + + // mchyzer Performance note: This creates very few new objects (one major goal) + // let me know if there are performance requests, we can create a harness to measure + + if (text == null || text.length() == 0 || searchList == null || + searchList.length == 0 || replacementList == null || replacementList.length == 0) { + return text; + } + + // if recursing, this shouldn't be less than 0 + if (timeToLive < 0) { + throw new IllegalStateException("Aborting to protect against StackOverflowError - " + + "output of one loop is the input of another"); + } + + int searchLength = searchList.length; + int replacementLength = replacementList.length; + + // make sure lengths are ok, these need to be equal + if (searchLength != replacementLength) { + throw new IllegalArgumentException("Search and Replace array lengths don't match: " + + searchLength + + " vs " + + replacementLength); + } + + // keep track of which still have matches + boolean[] noMoreMatchesForReplIndex = new boolean[searchLength]; + + // index on index that the match was found + int textIndex = -1; + int replaceIndex = -1; + int tempIndex = -1; + + // index of replace array that will replace the search string found + // NOTE: logic duplicated below START + for (int i = 0; i < searchLength; i++) { + if (noMoreMatchesForReplIndex[i] || searchList[i] == null || + searchList[i].length() == 0 || replacementList[i] == null) { + continue; + } + tempIndex = text.indexOf(searchList[i]); + + // see if we need to keep searching for this + if (tempIndex == -1) { + noMoreMatchesForReplIndex[i] = true; + } else { + if (textIndex == -1 || tempIndex < textIndex) { + textIndex = tempIndex; + replaceIndex = i; + } + } + } + // NOTE: logic mostly below END + + // no search strings found, we are done + if (textIndex == -1) { + return text; + } + + int start = 0; + + // get a good guess on the size of the result buffer so it doesn't have to double if it goes over a bit + int increase = 0; + + // count the replacement text elements that are larger than their corresponding text being replaced + for (int i = 0; i < searchList.length; i++) { + if (searchList[i] == null || replacementList[i] == null) { + continue; + } + int greater = replacementList[i].length() - searchList[i].length(); + if (greater > 0) { + increase += 3 * greater; // assume 3 matches + } + } + // have upper-bound at 20% increase, then let Java take over + increase = Math.min(increase, text.length() / 5); + + StringBuilder buf = new StringBuilder(text.length() + increase); + + while (textIndex != -1) { + + for (int i = start; i < textIndex; i++) { + buf.append(text.charAt(i)); + } + buf.append(replacementList[replaceIndex]); + + start = textIndex + searchList[replaceIndex].length(); + + textIndex = -1; + replaceIndex = -1; + tempIndex = -1; + // find the next earliest match + // NOTE: logic mostly duplicated above START + for (int i = 0; i < searchLength; i++) { + if (noMoreMatchesForReplIndex[i] || searchList[i] == null || + searchList[i].length() == 0 || replacementList[i] == null) { + continue; + } + tempIndex = text.indexOf(searchList[i], start); + + // see if we need to keep searching for this + if (tempIndex == -1) { + noMoreMatchesForReplIndex[i] = true; + } else { + if (textIndex == -1 || tempIndex < textIndex) { + textIndex = tempIndex; + replaceIndex = i; + } + } + } + // NOTE: logic duplicated above END + + } + int textLength = text.length(); + for (int i = start; i < textLength; i++) { + buf.append(text.charAt(i)); + } + String result = buf.toString(); + if (!repeat) { + return result; + } + + return replaceEach(result, searchList, replacementList, repeat, timeToLive - 1); + } + + // Replace, character based + //----------------------------------------------------------------------- + /** + *

Replaces all occurrences of a character in a String with another. + * This is a null-safe version of {@link String#replace(char, char)}.

+ * + *

A {@code null} string input returns {@code null}. + * An empty ("") string input returns an empty string.

+ * + *
+     * StringUtils.replaceChars(null, *, *)        = null
+     * StringUtils.replaceChars("", *, *)          = ""
+     * StringUtils.replaceChars("abcba", 'b', 'y') = "aycya"
+     * StringUtils.replaceChars("abcba", 'z', 'y') = "abcba"
+     * 
+ * + * @param str String to replace characters in, may be null + * @param searchChar the character to search for, may be null + * @param replaceChar the character to replace, may be null + * @return modified String, {@code null} if null string input + * @since 2.0 + */ + public static String replaceChars(String str, char searchChar, char replaceChar) { + if (str == null) { + return null; + } + return str.replace(searchChar, replaceChar); + } + + /** + *

Replaces multiple characters in a String in one go. + * This method can also be used to delete characters.

+ * + *

For example:
+ * replaceChars("hello", "ho", "jy") = jelly.

+ * + *

A {@code null} string input returns {@code null}. + * An empty ("") string input returns an empty string. + * A null or empty set of search characters returns the input string.

+ * + *

The length of the search characters should normally equal the length + * of the replace characters. + * If the search characters is longer, then the extra search characters + * are deleted. + * If the search characters is shorter, then the extra replace characters + * are ignored.

+ * + *
+     * StringUtils.replaceChars(null, *, *)           = null
+     * StringUtils.replaceChars("", *, *)             = ""
+     * StringUtils.replaceChars("abc", null, *)       = "abc"
+     * StringUtils.replaceChars("abc", "", *)         = "abc"
+     * StringUtils.replaceChars("abc", "b", null)     = "ac"
+     * StringUtils.replaceChars("abc", "b", "")       = "ac"
+     * StringUtils.replaceChars("abcba", "bc", "yz")  = "ayzya"
+     * StringUtils.replaceChars("abcba", "bc", "y")   = "ayya"
+     * StringUtils.replaceChars("abcba", "bc", "yzx") = "ayzya"
+     * 
+ * + * @param str String to replace characters in, may be null + * @param searchChars a set of characters to search for, may be null + * @param replaceChars a set of characters to replace, may be null + * @return modified String, {@code null} if null string input + * @since 2.0 + */ + public static String replaceChars(String str, String searchChars, String replaceChars) { + if (isEmpty(str) || isEmpty(searchChars)) { + return str; + } + if (replaceChars == null) { + replaceChars = EMPTY; + } + boolean modified = false; + int replaceCharsLength = replaceChars.length(); + int strLength = str.length(); + StringBuilder buf = new StringBuilder(strLength); + for (int i = 0; i < strLength; i++) { + char ch = str.charAt(i); + int index = searchChars.indexOf(ch); + if (index >= 0) { + modified = true; + if (index < replaceCharsLength) { + buf.append(replaceChars.charAt(index)); + } + } else { + buf.append(ch); + } + } + if (modified) { + return buf.toString(); + } + return str; + } + + // Overlay + //----------------------------------------------------------------------- + /** + *

Overlays part of a String with another String.

+ * + *

A {@code null} string input returns {@code null}. + * A negative index is treated as zero. + * An index greater than the string length is treated as the string length. + * The start index is always the smaller of the two indices.

+ * + *
+     * StringUtils.overlay(null, *, *, *)            = null
+     * StringUtils.overlay("", "abc", 0, 0)          = "abc"
+     * StringUtils.overlay("abcdef", null, 2, 4)     = "abef"
+     * StringUtils.overlay("abcdef", "", 2, 4)       = "abef"
+     * StringUtils.overlay("abcdef", "", 4, 2)       = "abef"
+     * StringUtils.overlay("abcdef", "zzzz", 2, 4)   = "abzzzzef"
+     * StringUtils.overlay("abcdef", "zzzz", 4, 2)   = "abzzzzef"
+     * StringUtils.overlay("abcdef", "zzzz", -1, 4)  = "zzzzef"
+     * StringUtils.overlay("abcdef", "zzzz", 2, 8)   = "abzzzz"
+     * StringUtils.overlay("abcdef", "zzzz", -2, -3) = "zzzzabcdef"
+     * StringUtils.overlay("abcdef", "zzzz", 8, 10)  = "abcdefzzzz"
+     * 
+ * + * @param str the String to do overlaying in, may be null + * @param overlay the String to overlay, may be null + * @param start the position to start overlaying at + * @param end the position to stop overlaying before + * @return overlayed String, {@code null} if null String input + * @since 2.0 + */ + public static String overlay(String str, String overlay, int start, int end) { + if (str == null) { + return null; + } + if (overlay == null) { + overlay = EMPTY; + } + int len = str.length(); + if (start < 0) { + start = 0; + } + if (start > len) { + start = len; + } + if (end < 0) { + end = 0; + } + if (end > len) { + end = len; + } + if (start > end) { + int temp = start; + start = end; + end = temp; + } + return new StringBuilder(len + start - end + overlay.length() + 1) + .append(str.substring(0, start)) + .append(overlay) + .append(str.substring(end)) + .toString(); + } + + // Chomping + //----------------------------------------------------------------------- + /** + *

Removes one newline from end of a String if it's there, + * otherwise leave it alone. A newline is "{@code \n}", + * "{@code \r}", or "{@code \r\n}".

+ * + *

NOTE: This method changed in 2.0. + * It now more closely matches Perl chomp.

+ * + *
+     * StringUtils.chomp(null)          = null
+     * StringUtils.chomp("")            = ""
+     * StringUtils.chomp("abc \r")      = "abc "
+     * StringUtils.chomp("abc\n")       = "abc"
+     * StringUtils.chomp("abc\r\n")     = "abc"
+     * StringUtils.chomp("abc\r\n\r\n") = "abc\r\n"
+     * StringUtils.chomp("abc\n\r")     = "abc\n"
+     * StringUtils.chomp("abc\n\rabc")  = "abc\n\rabc"
+     * StringUtils.chomp("\r")          = ""
+     * StringUtils.chomp("\n")          = ""
+     * StringUtils.chomp("\r\n")        = ""
+     * 
+ * + * @param str the String to chomp a newline from, may be null + * @return String without newline, {@code null} if null String input + */ + public static String chomp(String str) { + if (isEmpty(str)) { + return str; + } + + if (str.length() == 1) { + char ch = str.charAt(0); + if (ch == CharUtils.CR || ch == CharUtils.LF) { + return EMPTY; + } + return str; + } + + int lastIdx = str.length() - 1; + char last = str.charAt(lastIdx); + + if (last == CharUtils.LF) { + if (str.charAt(lastIdx - 1) == CharUtils.CR) { + lastIdx--; + } + } else if (last != CharUtils.CR) { + lastIdx++; + } + return str.substring(0, lastIdx); + } + + /** + *

Removes {@code separator} from the end of + * {@code str} if it's there, otherwise leave it alone.

+ * + *

NOTE: This method changed in version 2.0. + * It now more closely matches Perl chomp. + * For the previous behavior, use {@link #substringBeforeLast(String, String)}. + * This method uses {@link String#endsWith(String)}.

+ * + *
+     * StringUtils.chomp(null, *)         = null
+     * StringUtils.chomp("", *)           = ""
+     * StringUtils.chomp("foobar", "bar") = "foo"
+     * StringUtils.chomp("foobar", "baz") = "foobar"
+     * StringUtils.chomp("foo", "foo")    = ""
+     * StringUtils.chomp("foo ", "foo")   = "foo "
+     * StringUtils.chomp(" foo", "foo")   = " "
+     * StringUtils.chomp("foo", "foooo")  = "foo"
+     * StringUtils.chomp("foo", "")       = "foo"
+     * StringUtils.chomp("foo", null)     = "foo"
+     * 
+ * + * @param str the String to chomp from, may be null + * @param separator separator String, may be null + * @return String without trailing separator, {@code null} if null String input + * @deprecated This feature will be removed in Lang 4.0, use {@link StringUtils#removeEnd(String, String)} instead + */ + @Deprecated + public static String chomp(String str, String separator) { + return removeEnd(str,separator); + } + + // Chopping + //----------------------------------------------------------------------- + /** + *

Remove the last character from a String.

+ * + *

If the String ends in {@code \r\n}, then remove both + * of them.

+ * + *
+     * StringUtils.chop(null)          = null
+     * StringUtils.chop("")            = ""
+     * StringUtils.chop("abc \r")      = "abc "
+     * StringUtils.chop("abc\n")       = "abc"
+     * StringUtils.chop("abc\r\n")     = "abc"
+     * StringUtils.chop("abc")         = "ab"
+     * StringUtils.chop("abc\nabc")    = "abc\nab"
+     * StringUtils.chop("a")           = ""
+     * StringUtils.chop("\r")          = ""
+     * StringUtils.chop("\n")          = ""
+     * StringUtils.chop("\r\n")        = ""
+     * 
+ * + * @param str the String to chop last character from, may be null + * @return String without last character, {@code null} if null String input + */ + public static String chop(String str) { + if (str == null) { + return null; + } + int strLen = str.length(); + if (strLen < 2) { + return EMPTY; + } + int lastIdx = strLen - 1; + String ret = str.substring(0, lastIdx); + char last = str.charAt(lastIdx); + if (last == CharUtils.LF && ret.charAt(lastIdx - 1) == CharUtils.CR) { + return ret.substring(0, lastIdx - 1); + } + return ret; + } + + // Conversion + //----------------------------------------------------------------------- + + // Padding + //----------------------------------------------------------------------- + /** + *

Repeat a String {@code repeat} times to form a + * new String.

+ * + *
+     * StringUtils.repeat(null, 2) = null
+     * StringUtils.repeat("", 0)   = ""
+     * StringUtils.repeat("", 2)   = ""
+     * StringUtils.repeat("a", 3)  = "aaa"
+     * StringUtils.repeat("ab", 2) = "abab"
+     * StringUtils.repeat("a", -2) = ""
+     * 
+ * + * @param str the String to repeat, may be null + * @param repeat number of times to repeat str, negative treated as zero + * @return a new String consisting of the original String repeated, + * {@code null} if null String input + */ + public static String repeat(String str, int repeat) { + // Performance tuned for 2.0 (JDK1.4) + + if (str == null) { + return null; + } + if (repeat <= 0) { + return EMPTY; + } + int inputLength = str.length(); + if (repeat == 1 || inputLength == 0) { + return str; + } + if (inputLength == 1 && repeat <= PAD_LIMIT) { + return repeat(str.charAt(0), repeat); + } + + int outputLength = inputLength * repeat; + switch (inputLength) { + case 1 : + return repeat(str.charAt(0), repeat); + case 2 : + char ch0 = str.charAt(0); + char ch1 = str.charAt(1); + char[] output2 = new char[outputLength]; + for (int i = repeat * 2 - 2; i >= 0; i--, i--) { + output2[i] = ch0; + output2[i + 1] = ch1; + } + return new String(output2); + default : + StringBuilder buf = new StringBuilder(outputLength); + for (int i = 0; i < repeat; i++) { + buf.append(str); + } + return buf.toString(); + } + } + + /** + *

Repeat a String {@code repeat} times to form a + * new String, with a String separator injected each time.

+ * + *
+     * StringUtils.repeat(null, null, 2) = null
+     * StringUtils.repeat(null, "x", 2)  = null
+     * StringUtils.repeat("", null, 0)   = ""
+     * StringUtils.repeat("", "", 2)     = ""
+     * StringUtils.repeat("", "x", 3)    = "xxx"
+     * StringUtils.repeat("?", ", ", 3)  = "?, ?, ?"
+     * 
+ * + * @param str the String to repeat, may be null + * @param separator the String to inject, may be null + * @param repeat number of times to repeat str, negative treated as zero + * @return a new String consisting of the original String repeated, + * {@code null} if null String input + * @since 2.5 + */ + public static String repeat(String str, String separator, int repeat) { + if(str == null || separator == null) { + return repeat(str, repeat); + } else { + // given that repeat(String, int) is quite optimized, better to rely on it than try and splice this into it + String result = repeat(str + separator, repeat); + return removeEnd(result, separator); + } + } + + /** + *

Returns padding using the specified delimiter repeated + * to a given length.

+ * + *
+     * StringUtils.repeat(0, 'e')  = ""
+     * StringUtils.repeat(3, 'e')  = "eee"
+     * StringUtils.repeat(-2, 'e') = ""
+     * 
+ * + *

Note: this method doesn't not support padding with + * Unicode Supplementary Characters + * as they require a pair of {@code char}s to be represented. + * If you are needing to support full I18N of your applications + * consider using {@link #repeat(String, int)} instead. + *

+ * + * @param ch character to repeat + * @param repeat number of times to repeat char, negative treated as zero + * @return String with repeated character + * @see #repeat(String, int) + */ + public static String repeat(char ch, int repeat) { + char[] buf = new char[repeat]; + for (int i = repeat - 1; i >= 0; i--) { + buf[i] = ch; + } + return new String(buf); + } + + /** + *

Right pad a String with spaces (' ').

+ * + *

The String is padded to the size of {@code size}.

+ * + *
+     * StringUtils.rightPad(null, *)   = null
+     * StringUtils.rightPad("", 3)     = "   "
+     * StringUtils.rightPad("bat", 3)  = "bat"
+     * StringUtils.rightPad("bat", 5)  = "bat  "
+     * StringUtils.rightPad("bat", 1)  = "bat"
+     * StringUtils.rightPad("bat", -1) = "bat"
+     * 
+ * + * @param str the String to pad out, may be null + * @param size the size to pad to + * @return right padded String or original String if no padding is necessary, + * {@code null} if null String input + */ + public static String rightPad(String str, int size) { + return rightPad(str, size, ' '); + } + + /** + *

Right pad a String with a specified character.

+ * + *

The String is padded to the size of {@code size}.

+ * + *
+     * StringUtils.rightPad(null, *, *)     = null
+     * StringUtils.rightPad("", 3, 'z')     = "zzz"
+     * StringUtils.rightPad("bat", 3, 'z')  = "bat"
+     * StringUtils.rightPad("bat", 5, 'z')  = "batzz"
+     * StringUtils.rightPad("bat", 1, 'z')  = "bat"
+     * StringUtils.rightPad("bat", -1, 'z') = "bat"
+     * 
+ * + * @param str the String to pad out, may be null + * @param size the size to pad to + * @param padChar the character to pad with + * @return right padded String or original String if no padding is necessary, + * {@code null} if null String input + * @since 2.0 + */ + public static String rightPad(String str, int size, char padChar) { + if (str == null) { + return null; + } + int pads = size - str.length(); + if (pads <= 0) { + return str; // returns original String when possible + } + if (pads > PAD_LIMIT) { + return rightPad(str, size, String.valueOf(padChar)); + } + return str.concat(repeat(padChar, pads)); + } + + /** + *

Right pad a String with a specified String.

+ * + *

The String is padded to the size of {@code size}.

+ * + *
+     * StringUtils.rightPad(null, *, *)      = null
+     * StringUtils.rightPad("", 3, "z")      = "zzz"
+     * StringUtils.rightPad("bat", 3, "yz")  = "bat"
+     * StringUtils.rightPad("bat", 5, "yz")  = "batyz"
+     * StringUtils.rightPad("bat", 8, "yz")  = "batyzyzy"
+     * StringUtils.rightPad("bat", 1, "yz")  = "bat"
+     * StringUtils.rightPad("bat", -1, "yz") = "bat"
+     * StringUtils.rightPad("bat", 5, null)  = "bat  "
+     * StringUtils.rightPad("bat", 5, "")    = "bat  "
+     * 
+ * + * @param str the String to pad out, may be null + * @param size the size to pad to + * @param padStr the String to pad with, null or empty treated as single space + * @return right padded String or original String if no padding is necessary, + * {@code null} if null String input + */ + public static String rightPad(String str, int size, String padStr) { + if (str == null) { + return null; + } + if (isEmpty(padStr)) { + padStr = " "; + } + int padLen = padStr.length(); + int strLen = str.length(); + int pads = size - strLen; + if (pads <= 0) { + return str; // returns original String when possible + } + if (padLen == 1 && pads <= PAD_LIMIT) { + return rightPad(str, size, padStr.charAt(0)); + } + + if (pads == padLen) { + return str.concat(padStr); + } else if (pads < padLen) { + return str.concat(padStr.substring(0, pads)); + } else { + char[] padding = new char[pads]; + char[] padChars = padStr.toCharArray(); + for (int i = 0; i < pads; i++) { + padding[i] = padChars[i % padLen]; + } + return str.concat(new String(padding)); + } + } + + /** + *

Left pad a String with spaces (' ').

+ * + *

The String is padded to the size of {@code size}.

+ * + *
+     * StringUtils.leftPad(null, *)   = null
+     * StringUtils.leftPad("", 3)     = "   "
+     * StringUtils.leftPad("bat", 3)  = "bat"
+     * StringUtils.leftPad("bat", 5)  = "  bat"
+     * StringUtils.leftPad("bat", 1)  = "bat"
+     * StringUtils.leftPad("bat", -1) = "bat"
+     * 
+ * + * @param str the String to pad out, may be null + * @param size the size to pad to + * @return left padded String or original String if no padding is necessary, + * {@code null} if null String input + */ + public static String leftPad(String str, int size) { + return leftPad(str, size, ' '); + } + + /** + *

Left pad a String with a specified character.

+ * + *

Pad to a size of {@code size}.

+ * + *
+     * StringUtils.leftPad(null, *, *)     = null
+     * StringUtils.leftPad("", 3, 'z')     = "zzz"
+     * StringUtils.leftPad("bat", 3, 'z')  = "bat"
+     * StringUtils.leftPad("bat", 5, 'z')  = "zzbat"
+     * StringUtils.leftPad("bat", 1, 'z')  = "bat"
+     * StringUtils.leftPad("bat", -1, 'z') = "bat"
+     * 
+ * + * @param str the String to pad out, may be null + * @param size the size to pad to + * @param padChar the character to pad with + * @return left padded String or original String if no padding is necessary, + * {@code null} if null String input + * @since 2.0 + */ + public static String leftPad(String str, int size, char padChar) { + if (str == null) { + return null; + } + int pads = size - str.length(); + if (pads <= 0) { + return str; // returns original String when possible + } + if (pads > PAD_LIMIT) { + return leftPad(str, size, String.valueOf(padChar)); + } + return repeat(padChar, pads).concat(str); + } + + /** + *

Left pad a String with a specified String.

+ * + *

Pad to a size of {@code size}.

+ * + *
+     * StringUtils.leftPad(null, *, *)      = null
+     * StringUtils.leftPad("", 3, "z")      = "zzz"
+     * StringUtils.leftPad("bat", 3, "yz")  = "bat"
+     * StringUtils.leftPad("bat", 5, "yz")  = "yzbat"
+     * StringUtils.leftPad("bat", 8, "yz")  = "yzyzybat"
+     * StringUtils.leftPad("bat", 1, "yz")  = "bat"
+     * StringUtils.leftPad("bat", -1, "yz") = "bat"
+     * StringUtils.leftPad("bat", 5, null)  = "  bat"
+     * StringUtils.leftPad("bat", 5, "")    = "  bat"
+     * 
+ * + * @param str the String to pad out, may be null + * @param size the size to pad to + * @param padStr the String to pad with, null or empty treated as single space + * @return left padded String or original String if no padding is necessary, + * {@code null} if null String input + */ + public static String leftPad(String str, int size, String padStr) { + if (str == null) { + return null; + } + if (isEmpty(padStr)) { + padStr = " "; + } + int padLen = padStr.length(); + int strLen = str.length(); + int pads = size - strLen; + if (pads <= 0) { + return str; // returns original String when possible + } + if (padLen == 1 && pads <= PAD_LIMIT) { + return leftPad(str, size, padStr.charAt(0)); + } + + if (pads == padLen) { + return padStr.concat(str); + } else if (pads < padLen) { + return padStr.substring(0, pads).concat(str); + } else { + char[] padding = new char[pads]; + char[] padChars = padStr.toCharArray(); + for (int i = 0; i < pads; i++) { + padding[i] = padChars[i % padLen]; + } + return new String(padding).concat(str); + } + } + + /** + * Gets a CharSequence length or {@code 0} if the CharSequence is + * {@code null}. + * + * @param cs + * a CharSequence or {@code null} + * @return CharSequence length or {@code 0} if the CharSequence is + * {@code null}. + * @since 2.4 + * @since 3.0 Changed signature from length(String) to length(CharSequence) + */ + public static int length(CharSequence cs) { + return cs == null ? 0 : cs.length(); + } + + // Centering + //----------------------------------------------------------------------- + /** + *

Centers a String in a larger String of size {@code size} + * using the space character (' ').

+ * + *

If the size is less than the String length, the String is returned. + * A {@code null} String returns {@code null}. + * A negative size is treated as zero.

+ * + *

Equivalent to {@code center(str, size, " ")}.

+ * + *
+     * StringUtils.center(null, *)   = null
+     * StringUtils.center("", 4)     = "    "
+     * StringUtils.center("ab", -1)  = "ab"
+     * StringUtils.center("ab", 4)   = " ab "
+     * StringUtils.center("abcd", 2) = "abcd"
+     * StringUtils.center("a", 4)    = " a  "
+     * 
+ * + * @param str the String to center, may be null + * @param size the int size of new String, negative treated as zero + * @return centered String, {@code null} if null String input + */ + public static String center(String str, int size) { + return center(str, size, ' '); + } + + /** + *

Centers a String in a larger String of size {@code size}. + * Uses a supplied character as the value to pad the String with.

+ * + *

If the size is less than the String length, the String is returned. + * A {@code null} String returns {@code null}. + * A negative size is treated as zero.

+ * + *
+     * StringUtils.center(null, *, *)     = null
+     * StringUtils.center("", 4, ' ')     = "    "
+     * StringUtils.center("ab", -1, ' ')  = "ab"
+     * StringUtils.center("ab", 4, ' ')   = " ab"
+     * StringUtils.center("abcd", 2, ' ') = "abcd"
+     * StringUtils.center("a", 4, ' ')    = " a  "
+     * StringUtils.center("a", 4, 'y')    = "yayy"
+     * 
+ * + * @param str the String to center, may be null + * @param size the int size of new String, negative treated as zero + * @param padChar the character to pad the new String with + * @return centered String, {@code null} if null String input + * @since 2.0 + */ + public static String center(String str, int size, char padChar) { + if (str == null || size <= 0) { + return str; + } + int strLen = str.length(); + int pads = size - strLen; + if (pads <= 0) { + return str; + } + str = leftPad(str, strLen + pads / 2, padChar); + str = rightPad(str, size, padChar); + return str; + } + + /** + *

Centers a String in a larger String of size {@code size}. + * Uses a supplied String as the value to pad the String with.

+ * + *

If the size is less than the String length, the String is returned. + * A {@code null} String returns {@code null}. + * A negative size is treated as zero.

+ * + *
+     * StringUtils.center(null, *, *)     = null
+     * StringUtils.center("", 4, " ")     = "    "
+     * StringUtils.center("ab", -1, " ")  = "ab"
+     * StringUtils.center("ab", 4, " ")   = " ab"
+     * StringUtils.center("abcd", 2, " ") = "abcd"
+     * StringUtils.center("a", 4, " ")    = " a  "
+     * StringUtils.center("a", 4, "yz")   = "yayz"
+     * StringUtils.center("abc", 7, null) = "  abc  "
+     * StringUtils.center("abc", 7, "")   = "  abc  "
+     * 
+ * + * @param str the String to center, may be null + * @param size the int size of new String, negative treated as zero + * @param padStr the String to pad the new String with, must not be null or empty + * @return centered String, {@code null} if null String input + * @throws IllegalArgumentException if padStr is {@code null} or empty + */ + public static String center(String str, int size, String padStr) { + if (str == null || size <= 0) { + return str; + } + if (isEmpty(padStr)) { + padStr = " "; + } + int strLen = str.length(); + int pads = size - strLen; + if (pads <= 0) { + return str; + } + str = leftPad(str, strLen + pads / 2, padStr); + str = rightPad(str, size, padStr); + return str; + } + + // Case conversion + //----------------------------------------------------------------------- + /** + *

Converts a String to upper case as per {@link String#toUpperCase()}.

+ * + *

A {@code null} input String returns {@code null}.

+ * + *
+     * StringUtils.upperCase(null)  = null
+     * StringUtils.upperCase("")    = ""
+     * StringUtils.upperCase("aBc") = "ABC"
+     * 
+ * + *

Note: As described in the documentation for {@link String#toUpperCase()}, + * the result of this method is affected by the current locale. + * For platform-independent case transformations, the method {@link #lowerCase(String, Locale)} + * should be used with a specific locale (e.g. {@link Locale#ENGLISH}).

+ * + * @param str the String to upper case, may be null + * @return the upper cased String, {@code null} if null String input + */ + public static String upperCase(String str) { + if (str == null) { + return null; + } + return str.toUpperCase(); + } + + /** + *

Converts a String to upper case as per {@link String#toUpperCase(Locale)}.

+ * + *

A {@code null} input String returns {@code null}.

+ * + *
+     * StringUtils.upperCase(null, Locale.ENGLISH)  = null
+     * StringUtils.upperCase("", Locale.ENGLISH)    = ""
+     * StringUtils.upperCase("aBc", Locale.ENGLISH) = "ABC"
+     * 
+ * + * @param str the String to upper case, may be null + * @param locale the locale that defines the case transformation rules, must not be null + * @return the upper cased String, {@code null} if null String input + * @since 2.5 + */ + public static String upperCase(String str, Locale locale) { + if (str == null) { + return null; + } + return str.toUpperCase(locale); + } + + /** + *

Converts a String to lower case as per {@link String#toLowerCase()}.

+ * + *

A {@code null} input String returns {@code null}.

+ * + *
+     * StringUtils.lowerCase(null)  = null
+     * StringUtils.lowerCase("")    = ""
+     * StringUtils.lowerCase("aBc") = "abc"
+     * 
+ * + *

Note: As described in the documentation for {@link String#toLowerCase()}, + * the result of this method is affected by the current locale. + * For platform-independent case transformations, the method {@link #lowerCase(String, Locale)} + * should be used with a specific locale (e.g. {@link Locale#ENGLISH}).

+ * + * @param str the String to lower case, may be null + * @return the lower cased String, {@code null} if null String input + */ + public static String lowerCase(String str) { + if (str == null) { + return null; + } + return str.toLowerCase(); + } + + /** + *

Converts a String to lower case as per {@link String#toLowerCase(Locale)}.

+ * + *

A {@code null} input String returns {@code null}.

+ * + *
+     * StringUtils.lowerCase(null, Locale.ENGLISH)  = null
+     * StringUtils.lowerCase("", Locale.ENGLISH)    = ""
+     * StringUtils.lowerCase("aBc", Locale.ENGLISH) = "abc"
+     * 
+ * + * @param str the String to lower case, may be null + * @param locale the locale that defines the case transformation rules, must not be null + * @return the lower cased String, {@code null} if null String input + * @since 2.5 + */ + public static String lowerCase(String str, Locale locale) { + if (str == null) { + return null; + } + return str.toLowerCase(locale); + } + + /** + *

Capitalizes a String changing the first letter to title case as + * per {@link Character#toTitleCase(char)}. No other letters are changed.

+ * + *

For a word based algorithm, see {@link org.apache.commons.lang3.text.WordUtils#capitalize(String)}. + * A {@code null} input String returns {@code null}.

+ * + *
+     * StringUtils.capitalize(null)  = null
+     * StringUtils.capitalize("")    = ""
+     * StringUtils.capitalize("cat") = "Cat"
+     * StringUtils.capitalize("cAt") = "CAt"
+     * 
+ * + * @param str the String to capitalize, may be null + * @return the capitalized String, {@code null} if null String input + * @see org.apache.commons.lang3.text.WordUtils#capitalize(String) + * @see #uncapitalize(String) + * @since 2.0 + */ + public static String capitalize(String str) { + int strLen; + if (str == null || (strLen = str.length()) == 0) { + return str; + } + return new StringBuilder(strLen) + .append(Character.toTitleCase(str.charAt(0))) + .append(str.substring(1)) + .toString(); + } + + /** + *

Uncapitalizes a String changing the first letter to title case as + * per {@link Character#toLowerCase(char)}. No other letters are changed.

+ * + *

For a word based algorithm, see {@link org.apache.commons.lang3.text.WordUtils#uncapitalize(String)}. + * A {@code null} input String returns {@code null}.

+ * + *
+     * StringUtils.uncapitalize(null)  = null
+     * StringUtils.uncapitalize("")    = ""
+     * StringUtils.uncapitalize("Cat") = "cat"
+     * StringUtils.uncapitalize("CAT") = "cAT"
+     * 
+ * + * @param str the String to uncapitalize, may be null + * @return the uncapitalized String, {@code null} if null String input + * @see org.apache.commons.lang3.text.WordUtils#uncapitalize(String) + * @see #capitalize(String) + * @since 2.0 + */ + public static String uncapitalize(String str) { + int strLen; + if (str == null || (strLen = str.length()) == 0) { + return str; + } + return new StringBuilder(strLen) + .append(Character.toLowerCase(str.charAt(0))) + .append(str.substring(1)) + .toString(); + } + + /** + *

Swaps the case of a String changing upper and title case to + * lower case, and lower case to upper case.

+ * + *
    + *
  • Upper case character converts to Lower case
  • + *
  • Title case character converts to Lower case
  • + *
  • Lower case character converts to Upper case
  • + *
+ * + *

For a word based algorithm, see {@link org.apache.commons.lang3.text.WordUtils#swapCase(String)}. + * A {@code null} input String returns {@code null}.

+ * + *
+     * StringUtils.swapCase(null)                 = null
+     * StringUtils.swapCase("")                   = ""
+     * StringUtils.swapCase("The dog has a BONE") = "tHE DOG HAS A bone"
+     * 
+ * + *

NOTE: This method changed in Lang version 2.0. + * It no longer performs a word based algorithm. + * If you only use ASCII, you will notice no change. + * That functionality is available in org.apache.commons.lang3.text.WordUtils.

+ * + * @param str the String to swap case, may be null + * @return the changed String, {@code null} if null String input + */ + public static String swapCase(String str) { + if (StringUtils.isEmpty(str)) { + return str; + } + + char[] buffer = str.toCharArray(); + + for (int i = 0; i < buffer.length; i++) { + char ch = buffer[i]; + if (Character.isUpperCase(ch)) { + buffer[i] = Character.toLowerCase(ch); + } else if (Character.isTitleCase(ch)) { + buffer[i] = Character.toLowerCase(ch); + } else if (Character.isLowerCase(ch)) { + buffer[i] = Character.toUpperCase(ch); + } + } + return new String(buffer); + } + + // Count matches + //----------------------------------------------------------------------- + /** + *

Counts how many times the substring appears in the larger string.

+ * + *

A {@code null} or empty ("") String input returns {@code 0}.

+ * + *
+     * StringUtils.countMatches(null, *)       = 0
+     * StringUtils.countMatches("", *)         = 0
+     * StringUtils.countMatches("abba", null)  = 0
+     * StringUtils.countMatches("abba", "")    = 0
+     * StringUtils.countMatches("abba", "a")   = 2
+     * StringUtils.countMatches("abba", "ab")  = 1
+     * StringUtils.countMatches("abba", "xxx") = 0
+     * 
+ * + * @param str the CharSequence to check, may be null + * @param sub the substring to count, may be null + * @return the number of occurrences, 0 if either CharSequence is {@code null} + * @since 3.0 Changed signature from countMatches(String, String) to countMatches(CharSequence, CharSequence) + */ + public static int countMatches(CharSequence str, CharSequence sub) { + if (isEmpty(str) || isEmpty(sub)) { + return 0; + } + int count = 0; + int idx = 0; + while ((idx = CharSequenceUtils.indexOf(str, sub, idx)) != INDEX_NOT_FOUND) { + count++; + idx += sub.length(); + } + return count; + } + + // Character Tests + //----------------------------------------------------------------------- + /** + *

Checks if the CharSequence contains only Unicode letters.

+ * + *

{@code null} will return {@code false}. + * An empty CharSequence (length()=0) will return {@code false}.

+ * + *
+     * StringUtils.isAlpha(null)   = false
+     * StringUtils.isAlpha("")     = false
+     * StringUtils.isAlpha("  ")   = false
+     * StringUtils.isAlpha("abc")  = true
+     * StringUtils.isAlpha("ab2c") = false
+     * StringUtils.isAlpha("ab-c") = false
+     * 
+ * + * @param cs the CharSequence to check, may be null + * @return {@code true} if only contains letters, and is non-null + * @since 3.0 Changed signature from isAlpha(String) to isAlpha(CharSequence) + * @since 3.0 Changed "" to return false and not true + */ + public static boolean isAlpha(CharSequence cs) { + if (cs == null || cs.length() == 0) { + return false; + } + int sz = cs.length(); + for (int i = 0; i < sz; i++) { + if (Character.isLetter(cs.charAt(i)) == false) { + return false; + } + } + return true; + } + + /** + *

Checks if the CharSequence contains only Unicode letters and + * space (' ').

+ * + *

{@code null} will return {@code false} + * An empty CharSequence (length()=0) will return {@code true}.

+ * + *
+     * StringUtils.isAlphaSpace(null)   = false
+     * StringUtils.isAlphaSpace("")     = true
+     * StringUtils.isAlphaSpace("  ")   = true
+     * StringUtils.isAlphaSpace("abc")  = true
+     * StringUtils.isAlphaSpace("ab c") = true
+     * StringUtils.isAlphaSpace("ab2c") = false
+     * StringUtils.isAlphaSpace("ab-c") = false
+     * 
+ * + * @param cs the CharSequence to check, may be null + * @return {@code true} if only contains letters and space, + * and is non-null + * @since 3.0 Changed signature from isAlphaSpace(String) to isAlphaSpace(CharSequence) + */ + public static boolean isAlphaSpace(CharSequence cs) { + if (cs == null) { + return false; + } + int sz = cs.length(); + for (int i = 0; i < sz; i++) { + if (Character.isLetter(cs.charAt(i)) == false && cs.charAt(i) != ' ') { + return false; + } + } + return true; + } + + /** + *

Checks if the CharSequence contains only Unicode letters or digits.

+ * + *

{@code null} will return {@code false}. + * An empty CharSequence (length()=0) will return {@code false}.

+ * + *
+     * StringUtils.isAlphanumeric(null)   = false
+     * StringUtils.isAlphanumeric("")     = false
+     * StringUtils.isAlphanumeric("  ")   = false
+     * StringUtils.isAlphanumeric("abc")  = true
+     * StringUtils.isAlphanumeric("ab c") = false
+     * StringUtils.isAlphanumeric("ab2c") = true
+     * StringUtils.isAlphanumeric("ab-c") = false
+     * 
+ * + * @param cs the CharSequence to check, may be null + * @return {@code true} if only contains letters or digits, + * and is non-null + * @since 3.0 Changed signature from isAlphanumeric(String) to isAlphanumeric(CharSequence) + * @since 3.0 Changed "" to return false and not true + */ + public static boolean isAlphanumeric(CharSequence cs) { + if (cs == null || cs.length() == 0) { + return false; + } + int sz = cs.length(); + for (int i = 0; i < sz; i++) { + if (Character.isLetterOrDigit(cs.charAt(i)) == false) { + return false; + } + } + return true; + } + + /** + *

Checks if the CharSequence contains only Unicode letters, digits + * or space ({@code ' '}).

+ * + *

{@code null} will return {@code false}. + * An empty CharSequence (length()=0) will return {@code true}.

+ * + *
+     * StringUtils.isAlphanumericSpace(null)   = false
+     * StringUtils.isAlphanumericSpace("")     = true
+     * StringUtils.isAlphanumericSpace("  ")   = true
+     * StringUtils.isAlphanumericSpace("abc")  = true
+     * StringUtils.isAlphanumericSpace("ab c") = true
+     * StringUtils.isAlphanumericSpace("ab2c") = true
+     * StringUtils.isAlphanumericSpace("ab-c") = false
+     * 
+ * + * @param cs the CharSequence to check, may be null + * @return {@code true} if only contains letters, digits or space, + * and is non-null + * @since 3.0 Changed signature from isAlphanumericSpace(String) to isAlphanumericSpace(CharSequence) + */ + public static boolean isAlphanumericSpace(CharSequence cs) { + if (cs == null) { + return false; + } + int sz = cs.length(); + for (int i = 0; i < sz; i++) { + if (Character.isLetterOrDigit(cs.charAt(i)) == false && cs.charAt(i) != ' ') { + return false; + } + } + return true; + } + + /** + *

Checks if the CharSequence contains only ASCII printable characters.

+ * + *

{@code null} will return {@code false}. + * An empty CharSequence (length()=0) will return {@code true}.

+ * + *
+     * StringUtils.isAsciiPrintable(null)     = false
+     * StringUtils.isAsciiPrintable("")       = true
+     * StringUtils.isAsciiPrintable(" ")      = true
+     * StringUtils.isAsciiPrintable("Ceki")   = true
+     * StringUtils.isAsciiPrintable("ab2c")   = true
+     * StringUtils.isAsciiPrintable("!ab-c~") = true
+     * StringUtils.isAsciiPrintable("\u0020") = true
+     * StringUtils.isAsciiPrintable("\u0021") = true
+     * StringUtils.isAsciiPrintable("\u007e") = true
+     * StringUtils.isAsciiPrintable("\u007f") = false
+     * StringUtils.isAsciiPrintable("Ceki G\u00fclc\u00fc") = false
+     * 
+ * + * @param cs the CharSequence to check, may be null + * @return {@code true} if every character is in the range + * 32 thru 126 + * @since 2.1 + * @since 3.0 Changed signature from isAsciiPrintable(String) to isAsciiPrintable(CharSequence) + */ + public static boolean isAsciiPrintable(CharSequence cs) { + if (cs == null) { + return false; + } + int sz = cs.length(); + for (int i = 0; i < sz; i++) { + if (CharUtils.isAsciiPrintable(cs.charAt(i)) == false) { + return false; + } + } + return true; + } + + /** + *

Checks if the CharSequence contains only Unicode digits. + * A decimal point is not a Unicode digit and returns false.

+ * + *

{@code null} will return {@code false}. + * An empty CharSequence (length()=0) will return {@code false}.

+ * + *
+     * StringUtils.isNumeric(null)   = false
+     * StringUtils.isNumeric("")     = false
+     * StringUtils.isNumeric("  ")   = false
+     * StringUtils.isNumeric("123")  = true
+     * StringUtils.isNumeric("12 3") = false
+     * StringUtils.isNumeric("ab2c") = false
+     * StringUtils.isNumeric("12-3") = false
+     * StringUtils.isNumeric("12.3") = false
+     * 
+ * + * @param cs the CharSequence to check, may be null + * @return {@code true} if only contains digits, and is non-null + * @since 3.0 Changed signature from isNumeric(String) to isNumeric(CharSequence) + * @since 3.0 Changed "" to return false and not true + */ + public static boolean isNumeric(CharSequence cs) { + if (cs == null || cs.length() == 0) { + return false; + } + int sz = cs.length(); + for (int i = 0; i < sz; i++) { + if (Character.isDigit(cs.charAt(i)) == false) { + return false; + } + } + return true; + } + + /** + *

Checks if the CharSequence contains only Unicode digits or space + * ({@code ' '}). + * A decimal point is not a Unicode digit and returns false.

+ * + *

{@code null} will return {@code false}. + * An empty CharSequence (length()=0) will return {@code true}.

+ * + *
+     * StringUtils.isNumericSpace(null)   = false
+     * StringUtils.isNumericSpace("")     = true
+     * StringUtils.isNumericSpace("  ")   = true
+     * StringUtils.isNumericSpace("123")  = true
+     * StringUtils.isNumericSpace("12 3") = true
+     * StringUtils.isNumericSpace("ab2c") = false
+     * StringUtils.isNumericSpace("12-3") = false
+     * StringUtils.isNumericSpace("12.3") = false
+     * 
+ * + * @param cs the CharSequence to check, may be null + * @return {@code true} if only contains digits or space, + * and is non-null + * @since 3.0 Changed signature from isNumericSpace(String) to isNumericSpace(CharSequence) + */ + public static boolean isNumericSpace(CharSequence cs) { + if (cs == null) { + return false; + } + int sz = cs.length(); + for (int i = 0; i < sz; i++) { + if (Character.isDigit(cs.charAt(i)) == false && cs.charAt(i) != ' ') { + return false; + } + } + return true; + } + + /** + *

Checks if the CharSequence contains only whitespace.

+ * + *

{@code null} will return {@code false}. + * An empty CharSequence (length()=0) will return {@code true}.

+ * + *
+     * StringUtils.isWhitespace(null)   = false
+     * StringUtils.isWhitespace("")     = true
+     * StringUtils.isWhitespace("  ")   = true
+     * StringUtils.isWhitespace("abc")  = false
+     * StringUtils.isWhitespace("ab2c") = false
+     * StringUtils.isWhitespace("ab-c") = false
+     * 
+ * + * @param cs the CharSequence to check, may be null + * @return {@code true} if only contains whitespace, and is non-null + * @since 2.0 + * @since 3.0 Changed signature from isWhitespace(String) to isWhitespace(CharSequence) + */ + public static boolean isWhitespace(CharSequence cs) { + if (cs == null) { + return false; + } + int sz = cs.length(); + for (int i = 0; i < sz; i++) { + if (Character.isWhitespace(cs.charAt(i)) == false) { + return false; + } + } + return true; + } + + /** + *

Checks if the CharSequence contains only lowercase characters.

+ * + *

{@code null} will return {@code false}. + * An empty CharSequence (length()=0) will return {@code false}.

+ * + *
+     * StringUtils.isAllLowerCase(null)   = false
+     * StringUtils.isAllLowerCase("")     = false
+     * StringUtils.isAllLowerCase("  ")   = false
+     * StringUtils.isAllLowerCase("abc")  = true
+     * StringUtils.isAllLowerCase("abC") = false
+     * 
+ * + * @param cs the CharSequence to check, may be null + * @return {@code true} if only contains lowercase characters, and is non-null + * @since 2.5 + * @since 3.0 Changed signature from isAllLowerCase(String) to isAllLowerCase(CharSequence) + */ + public static boolean isAllLowerCase(CharSequence cs) { + if (cs == null || isEmpty(cs)) { + return false; + } + int sz = cs.length(); + for (int i = 0; i < sz; i++) { + if (Character.isLowerCase(cs.charAt(i)) == false) { + return false; + } + } + return true; + } + + /** + *

Checks if the CharSequence contains only uppercase characters.

+ * + *

{@code null} will return {@code false}. + * An empty String (length()=0) will return {@code false}.

+ * + *
+     * StringUtils.isAllUpperCase(null)   = false
+     * StringUtils.isAllUpperCase("")     = false
+     * StringUtils.isAllUpperCase("  ")   = false
+     * StringUtils.isAllUpperCase("ABC")  = true
+     * StringUtils.isAllUpperCase("aBC") = false
+     * 
+ * + * @param cs the CharSequence to check, may be null + * @return {@code true} if only contains uppercase characters, and is non-null + * @since 2.5 + * @since 3.0 Changed signature from isAllUpperCase(String) to isAllUpperCase(CharSequence) + */ + public static boolean isAllUpperCase(CharSequence cs) { + if (cs == null || isEmpty(cs)) { + return false; + } + int sz = cs.length(); + for (int i = 0; i < sz; i++) { + if (Character.isUpperCase(cs.charAt(i)) == false) { + return false; + } + } + return true; + } + + // Defaults + //----------------------------------------------------------------------- + /** + *

Returns either the passed in String, + * or if the String is {@code null}, an empty String ("").

+ * + *
+     * StringUtils.defaultString(null)  = ""
+     * StringUtils.defaultString("")    = ""
+     * StringUtils.defaultString("bat") = "bat"
+     * 
+ * + * @see ObjectUtils#toString(Object) + * @see String#valueOf(Object) + * @param str the String to check, may be null + * @return the passed in String, or the empty String if it + * was {@code null} + */ + public static String defaultString(String str) { + return str == null ? EMPTY : str; + } + + /** + *

Returns either the passed in String, or if the String is + * {@code null}, the value of {@code defaultStr}.

+ * + *
+     * StringUtils.defaultString(null, "NULL")  = "NULL"
+     * StringUtils.defaultString("", "NULL")    = ""
+     * StringUtils.defaultString("bat", "NULL") = "bat"
+     * 
+ * + * @see ObjectUtils#toString(Object,String) + * @see String#valueOf(Object) + * @param str the String to check, may be null + * @param defaultStr the default String to return + * if the input is {@code null}, may be null + * @return the passed in String, or the default if it was {@code null} + */ + public static String defaultString(String str, String defaultStr) { + return str == null ? defaultStr : str; + } + + /** + *

Returns either the passed in CharSequence, or if the CharSequence is + * whitespace, empty ("") or {@code null}, the value of {@code defaultStr}.

+ * + *
+     * StringUtils.defaultIfBlank(null, "NULL")  = "NULL"
+     * StringUtils.defaultIfBlank("", "NULL")    = "NULL"
+     * StringUtils.defaultIfBlank(" ", "NULL")   = "NULL"
+     * StringUtils.defaultIfBlank("bat", "NULL") = "bat"
+     * StringUtils.defaultIfBlank("", null)      = null
+     * 
+ * @param the specific kind of CharSequence + * @param str the CharSequence to check, may be null + * @param defaultStr the default CharSequence to return + * if the input is whitespace, empty ("") or {@code null}, may be null + * @return the passed in CharSequence, or the default + * @see StringUtils#defaultString(String, String) + */ + public static T defaultIfBlank(T str, T defaultStr) { + return StringUtils.isBlank(str) ? defaultStr : str; + } + + /** + *

Returns either the passed in CharSequence, or if the CharSequence is + * empty or {@code null}, the value of {@code defaultStr}.

+ * + *
+     * StringUtils.defaultIfEmpty(null, "NULL")  = "NULL"
+     * StringUtils.defaultIfEmpty("", "NULL")    = "NULL"
+     * StringUtils.defaultIfEmpty(" ", "NULL")   = " "
+     * StringUtils.defaultIfEmpty("bat", "NULL") = "bat"
+     * StringUtils.defaultIfEmpty("", null)      = null
+     * 
+ * @param the specific kind of CharSequence + * @param str the CharSequence to check, may be null + * @param defaultStr the default CharSequence to return + * if the input is empty ("") or {@code null}, may be null + * @return the passed in CharSequence, or the default + * @see StringUtils#defaultString(String, String) + */ + public static T defaultIfEmpty(T str, T defaultStr) { + return StringUtils.isEmpty(str) ? defaultStr : str; + } + + // Reversing + //----------------------------------------------------------------------- + /** + *

Reverses a String as per {@link StringBuilder#reverse()}.

+ * + *

A {@code null} String returns {@code null}.

+ * + *
+     * StringUtils.reverse(null)  = null
+     * StringUtils.reverse("")    = ""
+     * StringUtils.reverse("bat") = "tab"
+     * 
+ * + * @param str the String to reverse, may be null + * @return the reversed String, {@code null} if null String input + */ + public static String reverse(String str) { + if (str == null) { + return null; + } + return new StringBuilder(str).reverse().toString(); + } + + /** + *

Reverses a String that is delimited by a specific character.

+ * + *

The Strings between the delimiters are not reversed. + * Thus java.lang.String becomes String.lang.java (if the delimiter + * is {@code '.'}).

+ * + *
+     * StringUtils.reverseDelimited(null, *)      = null
+     * StringUtils.reverseDelimited("", *)        = ""
+     * StringUtils.reverseDelimited("a.b.c", 'x') = "a.b.c"
+     * StringUtils.reverseDelimited("a.b.c", ".") = "c.b.a"
+     * 
+ * + * @param str the String to reverse, may be null + * @param separatorChar the separator character to use + * @return the reversed String, {@code null} if null String input + * @since 2.0 + */ + public static String reverseDelimited(String str, char separatorChar) { + if (str == null) { + return null; + } + // could implement manually, but simple way is to reuse other, + // probably slower, methods. + String[] strs = split(str, separatorChar); + ArrayUtils.reverse(strs); + return join(strs, separatorChar); + } + + // Abbreviating + //----------------------------------------------------------------------- + /** + *

Abbreviates a String using ellipses. This will turn + * "Now is the time for all good men" into "Now is the time for..."

+ * + *

Specifically: + *

    + *
  • If {@code str} is less than {@code maxWidth} characters + * long, return it.
  • + *
  • Else abbreviate it to {@code (substring(str, 0, max-3) + "...")}.
  • + *
  • If {@code maxWidth} is less than {@code 4}, throw an + * {@code IllegalArgumentException}.
  • + *
  • In no case will it return a String of length greater than + * {@code maxWidth}.
  • + *
+ *

+ * + *
+     * StringUtils.abbreviate(null, *)      = null
+     * StringUtils.abbreviate("", 4)        = ""
+     * StringUtils.abbreviate("abcdefg", 6) = "abc..."
+     * StringUtils.abbreviate("abcdefg", 7) = "abcdefg"
+     * StringUtils.abbreviate("abcdefg", 8) = "abcdefg"
+     * StringUtils.abbreviate("abcdefg", 4) = "a..."
+     * StringUtils.abbreviate("abcdefg", 3) = IllegalArgumentException
+     * 
+ * + * @param str the String to check, may be null + * @param maxWidth maximum length of result String, must be at least 4 + * @return abbreviated String, {@code null} if null String input + * @throws IllegalArgumentException if the width is too small + * @since 2.0 + */ + public static String abbreviate(String str, int maxWidth) { + return abbreviate(str, 0, maxWidth); + } + + /** + *

Abbreviates a String using ellipses. This will turn + * "Now is the time for all good men" into "...is the time for..."

+ * + *

Works like {@code abbreviate(String, int)}, but allows you to specify + * a "left edge" offset. Note that this left edge is not necessarily going to + * be the leftmost character in the result, or the first character following the + * ellipses, but it will appear somewhere in the result. + * + *

In no case will it return a String of length greater than + * {@code maxWidth}.

+ * + *
+     * StringUtils.abbreviate(null, *, *)                = null
+     * StringUtils.abbreviate("", 0, 4)                  = ""
+     * StringUtils.abbreviate("abcdefghijklmno", -1, 10) = "abcdefg..."
+     * StringUtils.abbreviate("abcdefghijklmno", 0, 10)  = "abcdefg..."
+     * StringUtils.abbreviate("abcdefghijklmno", 1, 10)  = "abcdefg..."
+     * StringUtils.abbreviate("abcdefghijklmno", 4, 10)  = "abcdefg..."
+     * StringUtils.abbreviate("abcdefghijklmno", 5, 10)  = "...fghi..."
+     * StringUtils.abbreviate("abcdefghijklmno", 6, 10)  = "...ghij..."
+     * StringUtils.abbreviate("abcdefghijklmno", 8, 10)  = "...ijklmno"
+     * StringUtils.abbreviate("abcdefghijklmno", 10, 10) = "...ijklmno"
+     * StringUtils.abbreviate("abcdefghijklmno", 12, 10) = "...ijklmno"
+     * StringUtils.abbreviate("abcdefghij", 0, 3)        = IllegalArgumentException
+     * StringUtils.abbreviate("abcdefghij", 5, 6)        = IllegalArgumentException
+     * 
+ * + * @param str the String to check, may be null + * @param offset left edge of source String + * @param maxWidth maximum length of result String, must be at least 4 + * @return abbreviated String, {@code null} if null String input + * @throws IllegalArgumentException if the width is too small + * @since 2.0 + */ + public static String abbreviate(String str, int offset, int maxWidth) { + if (str == null) { + return null; + } + if (maxWidth < 4) { + throw new IllegalArgumentException("Minimum abbreviation width is 4"); + } + if (str.length() <= maxWidth) { + return str; + } + if (offset > str.length()) { + offset = str.length(); + } + if (str.length() - offset < maxWidth - 3) { + offset = str.length() - (maxWidth - 3); + } + final String abrevMarker = "..."; + if (offset <= 4) { + return str.substring(0, maxWidth - 3) + abrevMarker; + } + if (maxWidth < 7) { + throw new IllegalArgumentException("Minimum abbreviation width with offset is 7"); + } + if (offset + maxWidth - 3 < str.length()) { + return abrevMarker + abbreviate(str.substring(offset), maxWidth - 3); + } + return abrevMarker + str.substring(str.length() - (maxWidth - 3)); + } + + /** + *

Abbreviates a String to the length passed, replacing the middle characters with the supplied + * replacement String.

+ * + *

This abbreviation only occurs if the following criteria is met: + *

    + *
  • Neither the String for abbreviation nor the replacement String are null or empty
  • + *
  • The length to truncate to is less than the length of the supplied String
  • + *
  • The length to truncate to is greater than 0
  • + *
  • The abbreviated String will have enough room for the length supplied replacement String + * and the first and last characters of the supplied String for abbreviation
  • + *
+ * Otherwise, the returned String will be the same as the supplied String for abbreviation. + *

+ * + *
+     * StringUtils.abbreviateMiddle(null, null, 0)      = null
+     * StringUtils.abbreviateMiddle("abc", null, 0)      = "abc"
+     * StringUtils.abbreviateMiddle("abc", ".", 0)      = "abc"
+     * StringUtils.abbreviateMiddle("abc", ".", 3)      = "abc"
+     * StringUtils.abbreviateMiddle("abcdef", ".", 4)     = "ab.f"
+     * 
+ * + * @param str the String to abbreviate, may be null + * @param middle the String to replace the middle characters with, may be null + * @param length the length to abbreviate {@code str} to. + * @return the abbreviated String if the above criteria is met, or the original String supplied for abbreviation. + * @since 2.5 + */ + public static String abbreviateMiddle(String str, String middle, int length) { + if (isEmpty(str) || isEmpty(middle)) { + return str; + } + + if (length >= str.length() || length < middle.length()+2) { + return str; + } + + int targetSting = length-middle.length(); + int startOffset = targetSting/2+targetSting%2; + int endOffset = str.length()-targetSting/2; + + StringBuilder builder = new StringBuilder(length); + builder.append(str.substring(0,startOffset)); + builder.append(middle); + builder.append(str.substring(endOffset)); + + return builder.toString(); + } + + // Difference + //----------------------------------------------------------------------- + /** + *

Compares two Strings, and returns the portion where they differ. + * (More precisely, return the remainder of the second String, + * starting from where it's different from the first.)

+ * + *

For example, + * {@code difference("i am a machine", "i am a robot") -> "robot"}.

+ * + *
+     * StringUtils.difference(null, null) = null
+     * StringUtils.difference("", "") = ""
+     * StringUtils.difference("", "abc") = "abc"
+     * StringUtils.difference("abc", "") = ""
+     * StringUtils.difference("abc", "abc") = ""
+     * StringUtils.difference("ab", "abxyz") = "xyz"
+     * StringUtils.difference("abcde", "abxyz") = "xyz"
+     * StringUtils.difference("abcde", "xyz") = "xyz"
+     * 
+ * + * @param str1 the first String, may be null + * @param str2 the second String, may be null + * @return the portion of str2 where it differs from str1; returns the + * empty String if they are equal + * @since 2.0 + */ + public static String difference(String str1, String str2) { + if (str1 == null) { + return str2; + } + if (str2 == null) { + return str1; + } + int at = indexOfDifference(str1, str2); + if (at == INDEX_NOT_FOUND) { + return EMPTY; + } + return str2.substring(at); + } + + /** + *

Compares two CharSequences, and returns the index at which the + * CharSequences begin to differ.

+ * + *

For example, + * {@code indexOfDifference("i am a machine", "i am a robot") -> 7}

+ * + *
+     * StringUtils.indexOfDifference(null, null) = -1
+     * StringUtils.indexOfDifference("", "") = -1
+     * StringUtils.indexOfDifference("", "abc") = 0
+     * StringUtils.indexOfDifference("abc", "") = 0
+     * StringUtils.indexOfDifference("abc", "abc") = -1
+     * StringUtils.indexOfDifference("ab", "abxyz") = 2
+     * StringUtils.indexOfDifference("abcde", "abxyz") = 2
+     * StringUtils.indexOfDifference("abcde", "xyz") = 0
+     * 
+ * + * @param cs1 the first CharSequence, may be null + * @param cs2 the second CharSequence, may be null + * @return the index where cs1 and cs2 begin to differ; -1 if they are equal + * @since 2.0 + * @since 3.0 Changed signature from indexOfDifference(String, String) to + * indexOfDifference(CharSequence, CharSequence) + */ + public static int indexOfDifference(CharSequence cs1, CharSequence cs2) { + if (cs1 == cs2) { + return INDEX_NOT_FOUND; + } + if (cs1 == null || cs2 == null) { + return 0; + } + int i; + for (i = 0; i < cs1.length() && i < cs2.length(); ++i) { + if (cs1.charAt(i) != cs2.charAt(i)) { + break; + } + } + if (i < cs2.length() || i < cs1.length()) { + return i; + } + return INDEX_NOT_FOUND; + } + + /** + *

Compares all CharSequences in an array and returns the index at which the + * CharSequences begin to differ.

+ * + *

For example, + * indexOfDifference(new String[] {"i am a machine", "i am a robot"}) -> 7

+ * + *
+     * StringUtils.indexOfDifference(null) = -1
+     * StringUtils.indexOfDifference(new String[] {}) = -1
+     * StringUtils.indexOfDifference(new String[] {"abc"}) = -1
+     * StringUtils.indexOfDifference(new String[] {null, null}) = -1
+     * StringUtils.indexOfDifference(new String[] {"", ""}) = -1
+     * StringUtils.indexOfDifference(new String[] {"", null}) = 0
+     * StringUtils.indexOfDifference(new String[] {"abc", null, null}) = 0
+     * StringUtils.indexOfDifference(new String[] {null, null, "abc"}) = 0
+     * StringUtils.indexOfDifference(new String[] {"", "abc"}) = 0
+     * StringUtils.indexOfDifference(new String[] {"abc", ""}) = 0
+     * StringUtils.indexOfDifference(new String[] {"abc", "abc"}) = -1
+     * StringUtils.indexOfDifference(new String[] {"abc", "a"}) = 1
+     * StringUtils.indexOfDifference(new String[] {"ab", "abxyz"}) = 2
+     * StringUtils.indexOfDifference(new String[] {"abcde", "abxyz"}) = 2
+     * StringUtils.indexOfDifference(new String[] {"abcde", "xyz"}) = 0
+     * StringUtils.indexOfDifference(new String[] {"xyz", "abcde"}) = 0
+     * StringUtils.indexOfDifference(new String[] {"i am a machine", "i am a robot"}) = 7
+     * 
+ * + * @param css array of CharSequences, entries may be null + * @return the index where the strings begin to differ; -1 if they are all equal + * @since 2.4 + * @since 3.0 Changed signature from indexOfDifference(String...) to indexOfDifference(CharSequence...) + */ + public static int indexOfDifference(CharSequence... css) { + if (css == null || css.length <= 1) { + return INDEX_NOT_FOUND; + } + boolean anyStringNull = false; + boolean allStringsNull = true; + int arrayLen = css.length; + int shortestStrLen = Integer.MAX_VALUE; + int longestStrLen = 0; + + // find the min and max string lengths; this avoids checking to make + // sure we are not exceeding the length of the string each time through + // the bottom loop. + for (int i = 0; i < arrayLen; i++) { + if (css[i] == null) { + anyStringNull = true; + shortestStrLen = 0; + } else { + allStringsNull = false; + shortestStrLen = Math.min(css[i].length(), shortestStrLen); + longestStrLen = Math.max(css[i].length(), longestStrLen); + } + } + + // handle lists containing all nulls or all empty strings + if (allStringsNull || longestStrLen == 0 && !anyStringNull) { + return INDEX_NOT_FOUND; + } + + // handle lists containing some nulls or some empty strings + if (shortestStrLen == 0) { + return 0; + } + + // find the position with the first difference across all strings + int firstDiff = -1; + for (int stringPos = 0; stringPos < shortestStrLen; stringPos++) { + char comparisonChar = css[0].charAt(stringPos); + for (int arrayPos = 1; arrayPos < arrayLen; arrayPos++) { + if (css[arrayPos].charAt(stringPos) != comparisonChar) { + firstDiff = stringPos; + break; + } + } + if (firstDiff != -1) { + break; + } + } + + if (firstDiff == -1 && shortestStrLen != longestStrLen) { + // we compared all of the characters up to the length of the + // shortest string and didn't find a match, but the string lengths + // vary, so return the length of the shortest string. + return shortestStrLen; + } + return firstDiff; + } + + /** + *

Compares all Strings in an array and returns the initial sequence of + * characters that is common to all of them.

+ * + *

For example, + * getCommonPrefix(new String[] {"i am a machine", "i am a robot"}) -> "i am a "

+ * + *
+     * StringUtils.getCommonPrefix(null) = ""
+     * StringUtils.getCommonPrefix(new String[] {}) = ""
+     * StringUtils.getCommonPrefix(new String[] {"abc"}) = "abc"
+     * StringUtils.getCommonPrefix(new String[] {null, null}) = ""
+     * StringUtils.getCommonPrefix(new String[] {"", ""}) = ""
+     * StringUtils.getCommonPrefix(new String[] {"", null}) = ""
+     * StringUtils.getCommonPrefix(new String[] {"abc", null, null}) = ""
+     * StringUtils.getCommonPrefix(new String[] {null, null, "abc"}) = ""
+     * StringUtils.getCommonPrefix(new String[] {"", "abc"}) = ""
+     * StringUtils.getCommonPrefix(new String[] {"abc", ""}) = ""
+     * StringUtils.getCommonPrefix(new String[] {"abc", "abc"}) = "abc"
+     * StringUtils.getCommonPrefix(new String[] {"abc", "a"}) = "a"
+     * StringUtils.getCommonPrefix(new String[] {"ab", "abxyz"}) = "ab"
+     * StringUtils.getCommonPrefix(new String[] {"abcde", "abxyz"}) = "ab"
+     * StringUtils.getCommonPrefix(new String[] {"abcde", "xyz"}) = ""
+     * StringUtils.getCommonPrefix(new String[] {"xyz", "abcde"}) = ""
+     * StringUtils.getCommonPrefix(new String[] {"i am a machine", "i am a robot"}) = "i am a "
+     * 
+ * + * @param strs array of String objects, entries may be null + * @return the initial sequence of characters that are common to all Strings + * in the array; empty String if the array is null, the elements are all null + * or if there is no common prefix. + * @since 2.4 + */ + public static String getCommonPrefix(String... strs) { + if (strs == null || strs.length == 0) { + return EMPTY; + } + int smallestIndexOfDiff = indexOfDifference(strs); + if (smallestIndexOfDiff == INDEX_NOT_FOUND) { + // all strings were identical + if (strs[0] == null) { + return EMPTY; + } + return strs[0]; + } else if (smallestIndexOfDiff == 0) { + // there were no common initial characters + return EMPTY; + } else { + // we found a common initial character sequence + return strs[0].substring(0, smallestIndexOfDiff); + } + } + + // Misc + //----------------------------------------------------------------------- + /** + *

Find the Levenshtein distance between two Strings.

+ * + *

This is the number of changes needed to change one String into + * another, where each change is a single character modification (deletion, + * insertion or substitution).

+ * + *

The previous implementation of the Levenshtein distance algorithm + * was from http://www.merriampark.com/ld.htm

+ * + *

Chas Emerick has written an implementation in Java, which avoids an OutOfMemoryError + * which can occur when my Java implementation is used with very large strings.
+ * This implementation of the Levenshtein distance algorithm + * is from http://www.merriampark.com/ldjava.htm

+ * + *
+     * StringUtils.getLevenshteinDistance(null, *)             = IllegalArgumentException
+     * StringUtils.getLevenshteinDistance(*, null)             = IllegalArgumentException
+     * StringUtils.getLevenshteinDistance("","")               = 0
+     * StringUtils.getLevenshteinDistance("","a")              = 1
+     * StringUtils.getLevenshteinDistance("aaapppp", "")       = 7
+     * StringUtils.getLevenshteinDistance("frog", "fog")       = 1
+     * StringUtils.getLevenshteinDistance("fly", "ant")        = 3
+     * StringUtils.getLevenshteinDistance("elephant", "hippo") = 7
+     * StringUtils.getLevenshteinDistance("hippo", "elephant") = 7
+     * StringUtils.getLevenshteinDistance("hippo", "zzzzzzzz") = 8
+     * StringUtils.getLevenshteinDistance("hello", "hallo")    = 1
+     * 
+ * + * @param s the first String, must not be null + * @param t the second String, must not be null + * @return result distance + * @throws IllegalArgumentException if either String input {@code null} + * @since 3.0 Changed signature from getLevenshteinDistance(String, String) to + * getLevenshteinDistance(CharSequence, CharSequence) + */ + public static int getLevenshteinDistance(CharSequence s, CharSequence t) { + if (s == null || t == null) { + throw new IllegalArgumentException("Strings must not be null"); + } + + /* + The difference between this impl. and the previous is that, rather + than creating and retaining a matrix of size s.length() + 1 by t.length() + 1, + we maintain two single-dimensional arrays of length s.length() + 1. The first, d, + is the 'current working' distance array that maintains the newest distance cost + counts as we iterate through the characters of String s. Each time we increment + the index of String t we are comparing, d is copied to p, the second int[]. Doing so + allows us to retain the previous cost counts as required by the algorithm (taking + the minimum of the cost count to the left, up one, and diagonally up and to the left + of the current cost count being calculated). (Note that the arrays aren't really + copied anymore, just switched...this is clearly much better than cloning an array + or doing a System.arraycopy() each time through the outer loop.) + + Effectively, the difference between the two implementations is this one does not + cause an out of memory condition when calculating the LD over two very large strings. + */ + + int n = s.length(); // length of s + int m = t.length(); // length of t + + if (n == 0) { + return m; + } else if (m == 0) { + return n; + } + + if (n > m) { + // swap the input strings to consume less memory + CharSequence tmp = s; + s = t; + t = tmp; + n = m; + m = t.length(); + } + + int p[] = new int[n + 1]; //'previous' cost array, horizontally + int d[] = new int[n + 1]; // cost array, horizontally + int _d[]; //placeholder to assist in swapping p and d + + // indexes into strings s and t + int i; // iterates through s + int j; // iterates through t + + char t_j; // jth character of t + + int cost; // cost + + for (i = 0; i <= n; i++) { + p[i] = i; + } + + for (j = 1; j <= m; j++) { + t_j = t.charAt(j - 1); + d[0] = j; + + for (i = 1; i <= n; i++) { + cost = s.charAt(i - 1) == t_j ? 0 : 1; + // minimum of cell to the left+1, to the top+1, diagonally left and up +cost + d[i] = Math.min(Math.min(d[i - 1] + 1, p[i] + 1), p[i - 1] + cost); + } + + // copy current distance counts to 'previous row' distance counts + _d = p; + p = d; + d = _d; + } + + // our last action in the above loop was to switch d and p, so p now + // actually has the most recent cost counts + return p[n]; + } + + /** + *

Find the Levenshtein distance between two Strings if it's less than or equal to a given + * threshold.

+ * + *

This is the number of changes needed to change one String into + * another, where each change is a single character modification (deletion, + * insertion or substitution).

+ * + *

This implementation follows from Algorithms on Strings, Trees and Sequences by Dan Gusfield + * and Chas Emerick's implementation of the Levenshtein distance algorithm from + * http://www.merriampark.com/ld.htm

+ * + *
+     * StringUtils.getLevenshteinDistance(null, *, *)             = IllegalArgumentException
+     * StringUtils.getLevenshteinDistance(*, null, *)             = IllegalArgumentException
+     * StringUtils.getLevenshteinDistance(*, *, -1)               = IllegalArgumentException
+     * StringUtils.getLevenshteinDistance("","", 0)               = 0
+     * StringUtils.getLevenshteinDistance("aaapppp", "", 8)       = 7
+     * StringUtils.getLevenshteinDistance("aaapppp", "", 7)       = 7
+     * StringUtils.getLevenshteinDistance("aaapppp", "", 6))      = -1
+     * StringUtils.getLevenshteinDistance("elephant", "hippo", 7) = 7
+     * StringUtils.getLevenshteinDistance("elephant", "hippo", 6) = -1
+     * StringUtils.getLevenshteinDistance("hippo", "elephant", 7) = 7
+     * StringUtils.getLevenshteinDistance("hippo", "elephant", 6) = -1
+     * 
+ * + * @param s the first String, must not be null + * @param t the second String, must not be null + * @param threshold the target threshold, must not be negative + * @return result distance, or {@code -1} if the distance would be greater than the threshold + * @throws IllegalArgumentException if either String input {@code null} or negative threshold + */ + public static int getLevenshteinDistance(CharSequence s, CharSequence t, int threshold) { + if (s == null || t == null) { + throw new IllegalArgumentException("Strings must not be null"); + } + if (threshold < 0) { + throw new IllegalArgumentException("Threshold must not be negative"); + } + + /* + This implementation only computes the distance if it's less than or equal to the + threshold value, returning -1 if it's greater. The advantage is performance: unbounded + distance is O(nm), but a bound of k allows us to reduce it to O(km) time by only + computing a diagonal stripe of width 2k + 1 of the cost table. + It is also possible to use this to compute the unbounded Levenshtein distance by starting + the threshold at 1 and doubling each time until the distance is found; this is O(dm), where + d is the distance. + + One subtlety comes from needing to ignore entries on the border of our stripe + eg. + p[] = |#|#|#|* + d[] = *|#|#|#| + We must ignore the entry to the left of the leftmost member + We must ignore the entry above the rightmost member + + Another subtlety comes from our stripe running off the matrix if the strings aren't + of the same size. Since string s is always swapped to be the shorter of the two, + the stripe will always run off to the upper right instead of the lower left of the matrix. + + As a concrete example, suppose s is of length 5, t is of length 7, and our threshold is 1. + In this case we're going to walk a stripe of length 3. The matrix would look like so: + + 1 2 3 4 5 + 1 |#|#| | | | + 2 |#|#|#| | | + 3 | |#|#|#| | + 4 | | |#|#|#| + 5 | | | |#|#| + 6 | | | | |#| + 7 | | | | | | + + Note how the stripe leads off the table as there is no possible way to turn a string of length 5 + into one of length 7 in edit distance of 1. + + Additionally, this implementation decreases memory usage by using two + single-dimensional arrays and swapping them back and forth instead of allocating + an entire n by m matrix. This requires a few minor changes, such as immediately returning + when it's detected that the stripe has run off the matrix and initially filling the arrays with + large values so that entries we don't compute are ignored. + + See Algorithms on Strings, Trees and Sequences by Dan Gusfield for some discussion. + */ + + int n = s.length(); // length of s + int m = t.length(); // length of t + + // if one string is empty, the edit distance is necessarily the length of the other + if (n == 0) { + return m <= threshold ? m : -1; + } else if (m == 0) { + return n <= threshold ? n : -1; + } + + if (n > m) { + // swap the two strings to consume less memory + CharSequence tmp = s; + s = t; + t = tmp; + n = m; + m = t.length(); + } + + int p[] = new int[n + 1]; // 'previous' cost array, horizontally + int d[] = new int[n + 1]; // cost array, horizontally + int _d[]; // placeholder to assist in swapping p and d + + // fill in starting table values + int boundary = Math.min(n, threshold) + 1; + for (int i = 0; i < boundary; i++) { + p[i] = i; + } + // these fills ensure that the value above the rightmost entry of our + // stripe will be ignored in following loop iterations + Arrays.fill(p, boundary, p.length, Integer.MAX_VALUE); + Arrays.fill(d, Integer.MAX_VALUE); + + // iterates through t + for (int j = 1; j <= m; j++) { + char t_j = t.charAt(j - 1); // jth character of t + d[0] = j; + + // compute stripe indices, constrain to array size + int min = Math.max(1, j - threshold); + int max = Math.min(n, j + threshold); + + // the stripe may lead off of the table if s and t are of different sizes + if (min > max) { + return -1; + } + + // ignore entry left of leftmost + if (min > 1) { + d[min - 1] = Integer.MAX_VALUE; + } + + // iterates through [min, max] in s + for (int i = min; i <= max; i++) { + if (s.charAt(i - 1) == t_j) { + // diagonally left and up + d[i] = p[i - 1]; + } else { + // 1 + minimum of cell to the left, to the top, diagonally left and up + d[i] = 1 + Math.min(Math.min(d[i - 1], p[i]), p[i - 1]); + } + } + + // copy current distance counts to 'previous row' distance counts + _d = p; + p = d; + d = _d; + } + + // if p[n] is greater than the threshold, there's no guarantee on it being the correct + // distance + if (p[n] <= threshold) { + return p[n]; + } else { + return -1; + } + } + + // startsWith + //----------------------------------------------------------------------- + + /** + *

Check if a CharSequence starts with a specified prefix.

+ * + *

{@code null}s are handled without exceptions. Two {@code null} + * references are considered to be equal. The comparison is case sensitive.

+ * + *
+     * StringUtils.startsWith(null, null)      = true
+     * StringUtils.startsWith(null, "abc")     = false
+     * StringUtils.startsWith("abcdef", null)  = false
+     * StringUtils.startsWith("abcdef", "abc") = true
+     * StringUtils.startsWith("ABCDEF", "abc") = false
+     * 
+ * + * @see java.lang.String#startsWith(String) + * @param str the CharSequence to check, may be null + * @param prefix the prefix to find, may be null + * @return {@code true} if the CharSequence starts with the prefix, case sensitive, or + * both {@code null} + * @since 2.4 + * @since 3.0 Changed signature from startsWith(String, String) to startsWith(CharSequence, CharSequence) + */ + public static boolean startsWith(CharSequence str, CharSequence prefix) { + return startsWith(str, prefix, false); + } + + /** + *

Case insensitive check if a CharSequence starts with a specified prefix.

+ * + *

{@code null}s are handled without exceptions. Two {@code null} + * references are considered to be equal. The comparison is case insensitive.

+ * + *
+     * StringUtils.startsWithIgnoreCase(null, null)      = true
+     * StringUtils.startsWithIgnoreCase(null, "abc")     = false
+     * StringUtils.startsWithIgnoreCase("abcdef", null)  = false
+     * StringUtils.startsWithIgnoreCase("abcdef", "abc") = true
+     * StringUtils.startsWithIgnoreCase("ABCDEF", "abc") = true
+     * 
+ * + * @see java.lang.String#startsWith(String) + * @param str the CharSequence to check, may be null + * @param prefix the prefix to find, may be null + * @return {@code true} if the CharSequence starts with the prefix, case insensitive, or + * both {@code null} + * @since 2.4 + * @since 3.0 Changed signature from startsWithIgnoreCase(String, String) to startsWithIgnoreCase(CharSequence, CharSequence) + */ + public static boolean startsWithIgnoreCase(CharSequence str, CharSequence prefix) { + return startsWith(str, prefix, true); + } + + /** + *

Check if a CharSequence starts with a specified prefix (optionally case insensitive).

+ * + * @see java.lang.String#startsWith(String) + * @param str the CharSequence to check, may be null + * @param prefix the prefix to find, may be null + * @param ignoreCase indicates whether the compare should ignore case + * (case insensitive) or not. + * @return {@code true} if the CharSequence starts with the prefix or + * both {@code null} + */ + private static boolean startsWith(CharSequence str, CharSequence prefix, boolean ignoreCase) { + if (str == null || prefix == null) { + return str == null && prefix == null; + } + if (prefix.length() > str.length()) { + return false; + } + return CharSequenceUtils.regionMatches(str, ignoreCase, 0, prefix, 0, prefix.length()); + } + + /** + *

Check if a CharSequence starts with any of an array of specified strings.

+ * + *
+     * StringUtils.startsWithAny(null, null)      = false
+     * StringUtils.startsWithAny(null, new String[] {"abc"})  = false
+     * StringUtils.startsWithAny("abcxyz", null)     = false
+     * StringUtils.startsWithAny("abcxyz", new String[] {""}) = false
+     * StringUtils.startsWithAny("abcxyz", new String[] {"abc"}) = true
+     * StringUtils.startsWithAny("abcxyz", new String[] {null, "xyz", "abc"}) = true
+     * 
+ * + * @param string the CharSequence to check, may be null + * @param searchStrings the CharSequences to find, may be null or empty + * @return {@code true} if the CharSequence starts with any of the the prefixes, case insensitive, or + * both {@code null} + * @since 2.5 + * @since 3.0 Changed signature from startsWithAny(String, String[]) to startsWithAny(CharSequence, CharSequence...) + */ + public static boolean startsWithAny(CharSequence string, CharSequence... searchStrings) { + if (isEmpty(string) || ArrayUtils.isEmpty(searchStrings)) { + return false; + } + for (CharSequence searchString : searchStrings) { + if (StringUtils.startsWith(string, searchString)) { + return true; + } + } + return false; + } + + // endsWith + //----------------------------------------------------------------------- + + /** + *

Check if a CharSequence ends with a specified suffix.

+ * + *

{@code null}s are handled without exceptions. Two {@code null} + * references are considered to be equal. The comparison is case sensitive.

+ * + *
+     * StringUtils.endsWith(null, null)      = true
+     * StringUtils.endsWith(null, "def")     = false
+     * StringUtils.endsWith("abcdef", null)  = false
+     * StringUtils.endsWith("abcdef", "def") = true
+     * StringUtils.endsWith("ABCDEF", "def") = false
+     * StringUtils.endsWith("ABCDEF", "cde") = false
+     * 
+ * + * @see java.lang.String#endsWith(String) + * @param str the CharSequence to check, may be null + * @param suffix the suffix to find, may be null + * @return {@code true} if the CharSequence ends with the suffix, case sensitive, or + * both {@code null} + * @since 2.4 + * @since 3.0 Changed signature from endsWith(String, String) to endsWith(CharSequence, CharSequence) + */ + public static boolean endsWith(CharSequence str, CharSequence suffix) { + return endsWith(str, suffix, false); + } + + /** + *

Case insensitive check if a CharSequence ends with a specified suffix.

+ * + *

{@code null}s are handled without exceptions. Two {@code null} + * references are considered to be equal. The comparison is case insensitive.

+ * + *
+     * StringUtils.endsWithIgnoreCase(null, null)      = true
+     * StringUtils.endsWithIgnoreCase(null, "def")     = false
+     * StringUtils.endsWithIgnoreCase("abcdef", null)  = false
+     * StringUtils.endsWithIgnoreCase("abcdef", "def") = true
+     * StringUtils.endsWithIgnoreCase("ABCDEF", "def") = true
+     * StringUtils.endsWithIgnoreCase("ABCDEF", "cde") = false
+     * 
+ * + * @see java.lang.String#endsWith(String) + * @param str the CharSequence to check, may be null + * @param suffix the suffix to find, may be null + * @return {@code true} if the CharSequence ends with the suffix, case insensitive, or + * both {@code null} + * @since 2.4 + * @since 3.0 Changed signature from endsWithIgnoreCase(String, String) to endsWithIgnoreCase(CharSequence, CharSequence) + */ + public static boolean endsWithIgnoreCase(CharSequence str, CharSequence suffix) { + return endsWith(str, suffix, true); + } + + /** + *

Check if a CharSequence ends with a specified suffix (optionally case insensitive).

+ * + * @see java.lang.String#endsWith(String) + * @param str the CharSequence to check, may be null + * @param suffix the suffix to find, may be null + * @param ignoreCase indicates whether the compare should ignore case + * (case insensitive) or not. + * @return {@code true} if the CharSequence starts with the prefix or + * both {@code null} + */ + private static boolean endsWith(CharSequence str, CharSequence suffix, boolean ignoreCase) { + if (str == null || suffix == null) { + return str == null && suffix == null; + } + if (suffix.length() > str.length()) { + return false; + } + int strOffset = str.length() - suffix.length(); + return CharSequenceUtils.regionMatches(str, ignoreCase, strOffset, suffix, 0, suffix.length()); + } + + /** + *

+ * Similar to http://www.w3.org/TR/xpath/#function-normalize + * -space + *

+ *

+ * The function returns the argument string with whitespace normalized by using + * {@link #trim(String)} to remove leading and trailing whitespace + * and then replacing sequences of whitespace characters by a single space. + *

+ * In XML Whitespace characters are the same as those allowed by the S production, which is S ::= (#x20 | #x9 | #xD | #xA)+ + *

+ * Java's regexp pattern \s defines whitespace as [ \t\n\x0B\f\r] + *

+ * For reference: + *

    + *
  • \x0B = vertical tab
  • + *
  • \f = #xC = form feed
  • + *
  • #x20 = space
  • + *
  • #x9 = \t
  • + *
  • #xA = \n
  • + *
  • #xD = \r
  • + *
+ *

+ *

+ * The difference is that Java's whitespace includes vertical tab and form feed, which this functional will also + * normalize. Additionally {@link #trim(String)} removes control characters (char <= 32) from both + * ends of this String. + *

+ * + * @see Pattern + * @see #trim(String) + * @see http://www.w3.org/TR/xpath/#function-normalize-space + * @param str the source String to normalize whitespaces from, may be null + * @return the modified string with whitespace normalized, {@code null} if null String input + * + * @since 3.0 + */ + public static String normalizeSpace(String str) { + if (str == null) { + return null; + } + return WHITESPACE_BLOCK.matcher(trim(str)).replaceAll(" "); + } + + /** + *

Check if a CharSequence ends with any of an array of specified strings.

+ * + *
+     * StringUtils.endsWithAny(null, null)      = false
+     * StringUtils.endsWithAny(null, new String[] {"abc"})  = false
+     * StringUtils.endsWithAny("abcxyz", null)     = false
+     * StringUtils.endsWithAny("abcxyz", new String[] {""}) = true
+     * StringUtils.endsWithAny("abcxyz", new String[] {"xyz"}) = true
+     * StringUtils.endsWithAny("abcxyz", new String[] {null, "xyz", "abc"}) = true
+     * 
+ * + * @param string the CharSequence to check, may be null + * @param searchStrings the CharSequences to find, may be null or empty + * @return {@code true} if the CharSequence ends with any of the the prefixes, case insensitive, or + * both {@code null} + * @since 3.0 + */ + public static boolean endsWithAny(CharSequence string, CharSequence... searchStrings) { + if (isEmpty(string) || ArrayUtils.isEmpty(searchStrings)) { + return false; + } + for (CharSequence searchString : searchStrings) { + if (StringUtils.endsWith(string, searchString)) { + return true; + } + } + return false; + } + + /** + * Converts a byte[] to a String using the specified character encoding. + * + * @param bytes + * the byte array to read from + * @param charsetName + * the encoding to use, if null then use the platform default + * @return a new String + * @throws UnsupportedEncodingException + * If the named charset is not supported + * @throws NullPointerException + * if the input is null + * @since 3.1 + */ + public static String toString(byte[] bytes, String charsetName) throws UnsupportedEncodingException { + return charsetName == null ? new String(bytes) : new String(bytes, charsetName); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/StringUtilsEqualsIndexOfTest.java b/ApacheCommonsLang/org/apache/commons/lang3/StringUtilsEqualsIndexOfTest.java new file mode 100644 index 0000000..bb1afba --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/StringUtilsEqualsIndexOfTest.java @@ -0,0 +1,939 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3; + +import static org.junit.Assert.assertThat; + +import java.util.Locale; + +import junit.framework.TestCase; + +import org.hamcrest.core.IsNot; + +/** + * Unit tests {@link org.apache.commons.lang3.StringUtils} - Substring methods + * + * @version $Id: StringUtilsEqualsIndexOfTest.java 1309977 2012-04-05 17:53:39Z ggregory $ + */ +public class StringUtilsEqualsIndexOfTest extends TestCase { + private static final String BAR = "bar"; + /** + * Supplementary character U+20000 + * See http://java.sun.com/developer/technicalArticles/Intl/Supplementary/ + */ + private static final String CharU20000 = "\uD840\uDC00"; + /** + * Supplementary character U+20001 + * See http://java.sun.com/developer/technicalArticles/Intl/Supplementary/ + */ + private static final String CharU20001 = "\uD840\uDC01"; + /** + * Incomplete supplementary character U+20000, high surrogate only. + * See http://java.sun.com/developer/technicalArticles/Intl/Supplementary/ + */ + private static final String CharUSuppCharHigh = "\uDC00"; + + /** + * Incomplete supplementary character U+20000, low surrogate only. + * See http://java.sun.com/developer/technicalArticles/Intl/Supplementary/ + */ + private static final String CharUSuppCharLow = "\uD840"; + + private static final String FOO = "foo"; + + private static final String FOOBAR = "foobar"; + + private static final String[] FOOBAR_SUB_ARRAY = new String[] {"ob", "ba"}; + + public StringUtilsEqualsIndexOfTest(String name) { + super(name); + } + + public void testContains_Char() { + assertEquals(false, StringUtils.contains(null, ' ')); + assertEquals(false, StringUtils.contains("", ' ')); + assertEquals(false, StringUtils.contains("", null)); + assertEquals(false, StringUtils.contains(null, null)); + assertEquals(true, StringUtils.contains("abc", 'a')); + assertEquals(true, StringUtils.contains("abc", 'b')); + assertEquals(true, StringUtils.contains("abc", 'c')); + assertEquals(false, StringUtils.contains("abc", 'z')); + } + + public void testContains_String() { + assertEquals(false, StringUtils.contains(null, null)); + assertEquals(false, StringUtils.contains(null, "")); + assertEquals(false, StringUtils.contains(null, "a")); + assertEquals(false, StringUtils.contains("", null)); + assertEquals(true, StringUtils.contains("", "")); + assertEquals(false, StringUtils.contains("", "a")); + assertEquals(true, StringUtils.contains("abc", "a")); + assertEquals(true, StringUtils.contains("abc", "b")); + assertEquals(true, StringUtils.contains("abc", "c")); + assertEquals(true, StringUtils.contains("abc", "abc")); + assertEquals(false, StringUtils.contains("abc", "z")); + } + + /** + * See http://java.sun.com/developer/technicalArticles/Intl/Supplementary/ + */ + public void testContains_StringWithBadSupplementaryChars() { + // Test edge case: 1/2 of a (broken) supplementary char + assertEquals(false, StringUtils.contains(CharUSuppCharHigh, CharU20001)); + assertEquals(false, StringUtils.contains(CharUSuppCharLow, CharU20001)); + assertEquals(false, StringUtils.contains(CharU20001, CharUSuppCharHigh)); + assertEquals(0, CharU20001.indexOf(CharUSuppCharLow)); + assertEquals(true, StringUtils.contains(CharU20001, CharUSuppCharLow)); + assertEquals(true, StringUtils.contains(CharU20001 + CharUSuppCharLow + "a", "a")); + assertEquals(true, StringUtils.contains(CharU20001 + CharUSuppCharHigh + "a", "a")); + } + + /** + * See http://java.sun.com/developer/technicalArticles/Intl/Supplementary/ + */ + public void testContains_StringWithSupplementaryChars() { + assertEquals(true, StringUtils.contains(CharU20000 + CharU20001, CharU20000)); + assertEquals(true, StringUtils.contains(CharU20000 + CharU20001, CharU20001)); + assertEquals(true, StringUtils.contains(CharU20000, CharU20000)); + assertEquals(false, StringUtils.contains(CharU20000, CharU20001)); + } + + public void testContainsAny_StringCharArray() { + assertFalse(StringUtils.containsAny(null, (char[]) null)); + assertFalse(StringUtils.containsAny(null, new char[0])); + assertFalse(StringUtils.containsAny(null, new char[] { 'a', 'b' })); + + assertFalse(StringUtils.containsAny("", (char[]) null)); + assertFalse(StringUtils.containsAny("", new char[0])); + assertFalse(StringUtils.containsAny("", new char[] { 'a', 'b' })); + + assertFalse(StringUtils.containsAny("zzabyycdxx", (char[]) null)); + assertFalse(StringUtils.containsAny("zzabyycdxx", new char[0])); + assertTrue(StringUtils.containsAny("zzabyycdxx", new char[] { 'z', 'a' })); + assertTrue(StringUtils.containsAny("zzabyycdxx", new char[] { 'b', 'y' })); + assertFalse(StringUtils.containsAny("ab", new char[] { 'z' })); + } + + /** + * See http://java.sun.com/developer/technicalArticles/Intl/Supplementary/ + */ + public void testContainsAny_StringCharArrayWithBadSupplementaryChars() { + // Test edge case: 1/2 of a (broken) supplementary char + assertEquals(false, StringUtils.containsAny(CharUSuppCharHigh, CharU20001.toCharArray())); + assertEquals(false, StringUtils.containsAny("abc" + CharUSuppCharHigh + "xyz", CharU20001.toCharArray())); + assertEquals(-1, CharUSuppCharLow.indexOf(CharU20001)); + assertEquals(false, StringUtils.containsAny(CharUSuppCharLow, CharU20001.toCharArray())); + assertEquals(false, StringUtils.containsAny(CharU20001, CharUSuppCharHigh.toCharArray())); + assertEquals(0, CharU20001.indexOf(CharUSuppCharLow)); + assertEquals(true, StringUtils.containsAny(CharU20001, CharUSuppCharLow.toCharArray())); + } + + /** + * See http://java.sun.com/developer/technicalArticles/Intl/Supplementary/ + */ + public void testContainsAny_StringCharArrayWithSupplementaryChars() { + assertEquals(true, StringUtils.containsAny(CharU20000 + CharU20001, CharU20000.toCharArray())); + assertEquals(true, StringUtils.containsAny("a" + CharU20000 + CharU20001, "a".toCharArray())); + assertEquals(true, StringUtils.containsAny(CharU20000 + "a" + CharU20001, "a".toCharArray())); + assertEquals(true, StringUtils.containsAny(CharU20000 + CharU20001 + "a", "a".toCharArray())); + assertEquals(true, StringUtils.containsAny(CharU20000 + CharU20001, CharU20001.toCharArray())); + assertEquals(true, StringUtils.containsAny(CharU20000, CharU20000.toCharArray())); + // Sanity check: + assertEquals(-1, CharU20000.indexOf(CharU20001)); + assertEquals(0, CharU20000.indexOf(CharU20001.charAt(0))); + assertEquals(-1, CharU20000.indexOf(CharU20001.charAt(1))); + // Test: + assertEquals(false, StringUtils.containsAny(CharU20000, CharU20001.toCharArray())); + assertEquals(false, StringUtils.containsAny(CharU20001, CharU20000.toCharArray())); + } + + public void testContainsAny_StringString() { + assertFalse(StringUtils.containsAny(null, (String) null)); + assertFalse(StringUtils.containsAny(null, "")); + assertFalse(StringUtils.containsAny(null, "ab")); + + assertFalse(StringUtils.containsAny("", (String) null)); + assertFalse(StringUtils.containsAny("", "")); + assertFalse(StringUtils.containsAny("", "ab")); + + assertFalse(StringUtils.containsAny("zzabyycdxx", (String) null)); + assertFalse(StringUtils.containsAny("zzabyycdxx", "")); + assertTrue(StringUtils.containsAny("zzabyycdxx", "za")); + assertTrue(StringUtils.containsAny("zzabyycdxx", "by")); + assertFalse(StringUtils.containsAny("ab", "z")); + } + + /** + * See http://java.sun.com/developer/technicalArticles/Intl/Supplementary/ + */ + public void testContainsAny_StringWithBadSupplementaryChars() { + // Test edge case: 1/2 of a (broken) supplementary char + assertEquals(false, StringUtils.containsAny(CharUSuppCharHigh, CharU20001)); + assertEquals(-1, CharUSuppCharLow.indexOf(CharU20001)); + assertEquals(false, StringUtils.containsAny(CharUSuppCharLow, CharU20001)); + assertEquals(false, StringUtils.containsAny(CharU20001, CharUSuppCharHigh)); + assertEquals(0, CharU20001.indexOf(CharUSuppCharLow)); + assertEquals(true, StringUtils.containsAny(CharU20001, CharUSuppCharLow)); + } + + /** + * See http://java.sun.com/developer/technicalArticles/Intl/Supplementary/ + */ + public void testContainsAny_StringWithSupplementaryChars() { + assertEquals(true, StringUtils.containsAny(CharU20000 + CharU20001, CharU20000)); + assertEquals(true, StringUtils.containsAny(CharU20000 + CharU20001, CharU20001)); + assertEquals(true, StringUtils.containsAny(CharU20000, CharU20000)); + // Sanity check: + assertEquals(-1, CharU20000.indexOf(CharU20001)); + assertEquals(0, CharU20000.indexOf(CharU20001.charAt(0))); + assertEquals(-1, CharU20000.indexOf(CharU20001.charAt(1))); + // Test: + assertEquals(false, StringUtils.containsAny(CharU20000, CharU20001)); + assertEquals(false, StringUtils.containsAny(CharU20001, CharU20000)); + } + + public void testContainsIgnoreCase_LocaleIndependence() { + Locale orig = Locale.getDefault(); + + Locale[] locales = { Locale.ENGLISH, new Locale("tr"), Locale.getDefault() }; + + String[][] tdata = { + { "i", "I" }, + { "I", "i" }, + { "\u03C2", "\u03C3" }, + { "\u03A3", "\u03C2" }, + { "\u03A3", "\u03C3" }, + }; + + String[][] fdata = { + { "\u00DF", "SS" }, + }; + + try { + for (Locale locale : locales) { + Locale.setDefault(locale); + for (int j = 0; j < tdata.length; j++) { + assertTrue(Locale.getDefault() + ": " + j + " " + tdata[j][0] + " " + tdata[j][1], StringUtils + .containsIgnoreCase(tdata[j][0], tdata[j][1])); + } + for (int j = 0; j < fdata.length; j++) { + assertFalse(Locale.getDefault() + ": " + j + " " + fdata[j][0] + " " + fdata[j][1], StringUtils + .containsIgnoreCase(fdata[j][0], fdata[j][1])); + } + } + } finally { + Locale.setDefault(orig); + } + } + + public void testContainsIgnoreCase_StringString() { + assertFalse(StringUtils.containsIgnoreCase(null, null)); + + // Null tests + assertFalse(StringUtils.containsIgnoreCase(null, "")); + assertFalse(StringUtils.containsIgnoreCase(null, "a")); + assertFalse(StringUtils.containsIgnoreCase(null, "abc")); + + assertFalse(StringUtils.containsIgnoreCase("", null)); + assertFalse(StringUtils.containsIgnoreCase("a", null)); + assertFalse(StringUtils.containsIgnoreCase("abc", null)); + + // Match len = 0 + assertTrue(StringUtils.containsIgnoreCase("", "")); + assertTrue(StringUtils.containsIgnoreCase("a", "")); + assertTrue(StringUtils.containsIgnoreCase("abc", "")); + + // Match len = 1 + assertFalse(StringUtils.containsIgnoreCase("", "a")); + assertTrue(StringUtils.containsIgnoreCase("a", "a")); + assertTrue(StringUtils.containsIgnoreCase("abc", "a")); + assertFalse(StringUtils.containsIgnoreCase("", "A")); + assertTrue(StringUtils.containsIgnoreCase("a", "A")); + assertTrue(StringUtils.containsIgnoreCase("abc", "A")); + + // Match len > 1 + assertFalse(StringUtils.containsIgnoreCase("", "abc")); + assertFalse(StringUtils.containsIgnoreCase("a", "abc")); + assertTrue(StringUtils.containsIgnoreCase("xabcz", "abc")); + assertFalse(StringUtils.containsIgnoreCase("", "ABC")); + assertFalse(StringUtils.containsIgnoreCase("a", "ABC")); + assertTrue(StringUtils.containsIgnoreCase("xabcz", "ABC")); + } + + public void testContainsNone_CharArray() { + String str1 = "a"; + String str2 = "b"; + String str3 = "ab."; + char[] chars1= {'b'}; + char[] chars2= {'.'}; + char[] chars3= {'c', 'd'}; + char[] emptyChars = new char[0]; + assertEquals(true, StringUtils.containsNone(null, (char[]) null)); + assertEquals(true, StringUtils.containsNone("", (char[]) null)); + assertEquals(true, StringUtils.containsNone(null, emptyChars)); + assertEquals(true, StringUtils.containsNone(str1, emptyChars)); + assertEquals(true, StringUtils.containsNone("", emptyChars)); + assertEquals(true, StringUtils.containsNone("", chars1)); + assertEquals(true, StringUtils.containsNone(str1, chars1)); + assertEquals(true, StringUtils.containsNone(str1, chars2)); + assertEquals(true, StringUtils.containsNone(str1, chars3)); + assertEquals(false, StringUtils.containsNone(str2, chars1)); + assertEquals(true, StringUtils.containsNone(str2, chars2)); + assertEquals(true, StringUtils.containsNone(str2, chars3)); + assertEquals(false, StringUtils.containsNone(str3, chars1)); + assertEquals(false, StringUtils.containsNone(str3, chars2)); + assertEquals(true, StringUtils.containsNone(str3, chars3)); + } + + /** + * See http://java.sun.com/developer/technicalArticles/Intl/Supplementary/ + */ + public void testContainsNone_CharArrayWithBadSupplementaryChars() { + // Test edge case: 1/2 of a (broken) supplementary char + assertEquals(true, StringUtils.containsNone(CharUSuppCharHigh, CharU20001.toCharArray())); + assertEquals(-1, CharUSuppCharLow.indexOf(CharU20001)); + assertEquals(true, StringUtils.containsNone(CharUSuppCharLow, CharU20001.toCharArray())); + assertEquals(-1, CharU20001.indexOf(CharUSuppCharHigh)); + assertEquals(true, StringUtils.containsNone(CharU20001, CharUSuppCharHigh.toCharArray())); + assertEquals(0, CharU20001.indexOf(CharUSuppCharLow)); + assertEquals(false, StringUtils.containsNone(CharU20001, CharUSuppCharLow.toCharArray())); + } + + /** + * See http://java.sun.com/developer/technicalArticles/Intl/Supplementary/ + */ + public void testContainsNone_CharArrayWithSupplementaryChars() { + assertEquals(false, StringUtils.containsNone(CharU20000 + CharU20001, CharU20000.toCharArray())); + assertEquals(false, StringUtils.containsNone(CharU20000 + CharU20001, CharU20001.toCharArray())); + assertEquals(false, StringUtils.containsNone(CharU20000, CharU20000.toCharArray())); + // Sanity check: + assertEquals(-1, CharU20000.indexOf(CharU20001)); + assertEquals(0, CharU20000.indexOf(CharU20001.charAt(0))); + assertEquals(-1, CharU20000.indexOf(CharU20001.charAt(1))); + // Test: + assertEquals(true, StringUtils.containsNone(CharU20000, CharU20001.toCharArray())); + assertEquals(true, StringUtils.containsNone(CharU20001, CharU20000.toCharArray())); + } + + public void testContainsNone_String() { + String str1 = "a"; + String str2 = "b"; + String str3 = "ab."; + String chars1= "b"; + String chars2= "."; + String chars3= "cd"; + assertEquals(true, StringUtils.containsNone(null, (String) null)); + assertEquals(true, StringUtils.containsNone("", (String) null)); + assertEquals(true, StringUtils.containsNone(null, "")); + assertEquals(true, StringUtils.containsNone(str1, "")); + assertEquals(true, StringUtils.containsNone("", "")); + assertEquals(true, StringUtils.containsNone("", chars1)); + assertEquals(true, StringUtils.containsNone(str1, chars1)); + assertEquals(true, StringUtils.containsNone(str1, chars2)); + assertEquals(true, StringUtils.containsNone(str1, chars3)); + assertEquals(false, StringUtils.containsNone(str2, chars1)); + assertEquals(true, StringUtils.containsNone(str2, chars2)); + assertEquals(true, StringUtils.containsNone(str2, chars3)); + assertEquals(false, StringUtils.containsNone(str3, chars1)); + assertEquals(false, StringUtils.containsNone(str3, chars2)); + assertEquals(true, StringUtils.containsNone(str3, chars3)); + } + + /** + * See http://java.sun.com/developer/technicalArticles/Intl/Supplementary/ + */ + public void testContainsNone_StringWithBadSupplementaryChars() { + // Test edge case: 1/2 of a (broken) supplementary char + assertEquals(true, StringUtils.containsNone(CharUSuppCharHigh, CharU20001)); + assertEquals(-1, CharUSuppCharLow.indexOf(CharU20001)); + assertEquals(true, StringUtils.containsNone(CharUSuppCharLow, CharU20001)); + assertEquals(-1, CharU20001.indexOf(CharUSuppCharHigh)); + assertEquals(true, StringUtils.containsNone(CharU20001, CharUSuppCharHigh)); + assertEquals(0, CharU20001.indexOf(CharUSuppCharLow)); + assertEquals(false, StringUtils.containsNone(CharU20001, CharUSuppCharLow)); + } + + /** + * See http://java.sun.com/developer/technicalArticles/Intl/Supplementary/ + */ + public void testContainsNone_StringWithSupplementaryChars() { + assertEquals(false, StringUtils.containsNone(CharU20000 + CharU20001, CharU20000)); + assertEquals(false, StringUtils.containsNone(CharU20000 + CharU20001, CharU20001)); + assertEquals(false, StringUtils.containsNone(CharU20000, CharU20000)); + // Sanity check: + assertEquals(-1, CharU20000.indexOf(CharU20001)); + assertEquals(0, CharU20000.indexOf(CharU20001.charAt(0))); + assertEquals(-1, CharU20000.indexOf(CharU20001.charAt(1))); + // Test: + assertEquals(true, StringUtils.containsNone(CharU20000, CharU20001)); + assertEquals(true, StringUtils.containsNone(CharU20001, CharU20000)); + } + + public void testContainsOnly_CharArray() { + String str1 = "a"; + String str2 = "b"; + String str3 = "ab"; + char[] chars1= {'b'}; + char[] chars2= {'a'}; + char[] chars3= {'a', 'b'}; + char[] emptyChars = new char[0]; + assertEquals(false, StringUtils.containsOnly(null, (char[]) null)); + assertEquals(false, StringUtils.containsOnly("", (char[]) null)); + assertEquals(false, StringUtils.containsOnly(null, emptyChars)); + assertEquals(false, StringUtils.containsOnly(str1, emptyChars)); + assertEquals(true, StringUtils.containsOnly("", emptyChars)); + assertEquals(true, StringUtils.containsOnly("", chars1)); + assertEquals(false, StringUtils.containsOnly(str1, chars1)); + assertEquals(true, StringUtils.containsOnly(str1, chars2)); + assertEquals(true, StringUtils.containsOnly(str1, chars3)); + assertEquals(true, StringUtils.containsOnly(str2, chars1)); + assertEquals(false, StringUtils.containsOnly(str2, chars2)); + assertEquals(true, StringUtils.containsOnly(str2, chars3)); + assertEquals(false, StringUtils.containsOnly(str3, chars1)); + assertEquals(false, StringUtils.containsOnly(str3, chars2)); + assertEquals(true, StringUtils.containsOnly(str3, chars3)); + } + + public void testContainsOnly_String() { + String str1 = "a"; + String str2 = "b"; + String str3 = "ab"; + String chars1= "b"; + String chars2= "a"; + String chars3= "ab"; + assertEquals(false, StringUtils.containsOnly(null, (String) null)); + assertEquals(false, StringUtils.containsOnly("", (String) null)); + assertEquals(false, StringUtils.containsOnly(null, "")); + assertEquals(false, StringUtils.containsOnly(str1, "")); + assertEquals(true, StringUtils.containsOnly("", "")); + assertEquals(true, StringUtils.containsOnly("", chars1)); + assertEquals(false, StringUtils.containsOnly(str1, chars1)); + assertEquals(true, StringUtils.containsOnly(str1, chars2)); + assertEquals(true, StringUtils.containsOnly(str1, chars3)); + assertEquals(true, StringUtils.containsOnly(str2, chars1)); + assertEquals(false, StringUtils.containsOnly(str2, chars2)); + assertEquals(true, StringUtils.containsOnly(str2, chars3)); + assertEquals(false, StringUtils.containsOnly(str3, chars1)); + assertEquals(false, StringUtils.containsOnly(str3, chars2)); + assertEquals(true, StringUtils.containsOnly(str3, chars3)); + } + + public void testContainsWhitespace() { + assertFalse( StringUtils.containsWhitespace("") ); + assertTrue( StringUtils.containsWhitespace(" ") ); + assertFalse( StringUtils.containsWhitespace("a") ); + assertTrue( StringUtils.containsWhitespace("a ") ); + assertTrue( StringUtils.containsWhitespace(" a") ); + assertTrue( StringUtils.containsWhitespace("a\t") ); + assertTrue( StringUtils.containsWhitespace("\n") ); + } + + // The purpose of this class is to test StringUtils#equals(CharSequence, CharSequence) + // with a CharSequence implementation whose equals(Object) override requires that the + // other object be an instance of CustomCharSequence, even though, as char sequences, + // `seq` may equal the other object. + private static class CustomCharSequence implements CharSequence { + private CharSequence seq; + + public CustomCharSequence(CharSequence seq) { + this.seq = seq; + } + + @Override + public char charAt(int index) { + return seq.charAt(index); + } + + @Override + public int length() { + return seq.length(); + } + + @Override + public CharSequence subSequence(int start, int end) { + return new CustomCharSequence(seq.subSequence(start, end)); + } + + @Override + public boolean equals(Object obj) { + if (obj == null || !(obj instanceof CustomCharSequence)) { + return false; + } + CustomCharSequence other = (CustomCharSequence) obj; + return seq.equals(other.seq); + } + + @Override + public String toString() { + return seq.toString(); + } + } + + public void testCustomCharSequence() { + assertThat((CharSequence) new CustomCharSequence(FOO), IsNot.not(FOO)); + assertThat((CharSequence) FOO, IsNot.not(new CustomCharSequence(FOO))); + assertEquals(new CustomCharSequence(FOO), new CustomCharSequence(FOO)); + } + + public void testEquals() { + final CharSequence fooCs = FOO, barCs = BAR, foobarCs = FOOBAR; + assertTrue(StringUtils.equals(null, null)); + assertTrue(StringUtils.equals(fooCs, fooCs)); + assertTrue(StringUtils.equals(fooCs, (CharSequence) new StringBuilder(FOO))); + assertTrue(StringUtils.equals(fooCs, (CharSequence) new String(new char[] { 'f', 'o', 'o' }))); + assertTrue(StringUtils.equals(fooCs, (CharSequence) new CustomCharSequence(FOO))); + assertTrue(StringUtils.equals((CharSequence) new CustomCharSequence(FOO), fooCs)); + assertFalse(StringUtils.equals(fooCs, (CharSequence) new String(new char[] { 'f', 'O', 'O' }))); + assertFalse(StringUtils.equals(fooCs, barCs)); + assertFalse(StringUtils.equals(fooCs, null)); + assertFalse(StringUtils.equals(null, fooCs)); + assertFalse(StringUtils.equals(fooCs, foobarCs)); + assertFalse(StringUtils.equals(foobarCs, fooCs)); + } + + public void testEqualsOnStrings() { + assertTrue(StringUtils.equals(null, null)); + assertTrue(StringUtils.equals(FOO, FOO)); + assertTrue(StringUtils.equals(FOO, new String(new char[] { 'f', 'o', 'o' }))); + assertFalse(StringUtils.equals(FOO, new String(new char[] { 'f', 'O', 'O' }))); + assertFalse(StringUtils.equals(FOO, BAR)); + assertFalse(StringUtils.equals(FOO, null)); + assertFalse(StringUtils.equals(null, FOO)); + assertFalse(StringUtils.equals(FOO, FOOBAR)); + assertFalse(StringUtils.equals(FOOBAR, FOO)); + } + + public void testEqualsIgnoreCase() { + assertEquals(true, StringUtils.equalsIgnoreCase(null, null)); + assertEquals(true, StringUtils.equalsIgnoreCase(FOO, FOO)); + assertEquals(true, StringUtils.equalsIgnoreCase(FOO, new String(new char[] { 'f', 'o', 'o' }))); + assertEquals(true, StringUtils.equalsIgnoreCase(FOO, new String(new char[] { 'f', 'O', 'O' }))); + assertEquals(false, StringUtils.equalsIgnoreCase(FOO, BAR)); + assertEquals(false, StringUtils.equalsIgnoreCase(FOO, null)); + assertEquals(false, StringUtils.equalsIgnoreCase(null, FOO)); + } + + //----------------------------------------------------------------------- + public void testIndexOf_char() { + assertEquals(-1, StringUtils.indexOf(null, ' ')); + assertEquals(-1, StringUtils.indexOf("", ' ')); + assertEquals(0, StringUtils.indexOf("aabaabaa", 'a')); + assertEquals(2, StringUtils.indexOf("aabaabaa", 'b')); + + assertEquals(2, StringUtils.indexOf(new StringBuilder("aabaabaa"), 'b')); + } + + public void testIndexOf_charInt() { + assertEquals(-1, StringUtils.indexOf(null, ' ', 0)); + assertEquals(-1, StringUtils.indexOf(null, ' ', -1)); + assertEquals(-1, StringUtils.indexOf("", ' ', 0)); + assertEquals(-1, StringUtils.indexOf("", ' ', -1)); + assertEquals(0, StringUtils.indexOf("aabaabaa", 'a', 0)); + assertEquals(2, StringUtils.indexOf("aabaabaa", 'b', 0)); + assertEquals(5, StringUtils.indexOf("aabaabaa", 'b', 3)); + assertEquals(-1, StringUtils.indexOf("aabaabaa", 'b', 9)); + assertEquals(2, StringUtils.indexOf("aabaabaa", 'b', -1)); + + assertEquals(5, StringUtils.indexOf(new StringBuilder("aabaabaa"), 'b', 3)); + } + + public void testIndexOf_String() { + assertEquals(-1, StringUtils.indexOf(null, null)); + assertEquals(-1, StringUtils.indexOf("", null)); + assertEquals(0, StringUtils.indexOf("", "")); + assertEquals(0, StringUtils.indexOf("aabaabaa", "a")); + assertEquals(2, StringUtils.indexOf("aabaabaa", "b")); + assertEquals(1, StringUtils.indexOf("aabaabaa", "ab")); + assertEquals(0, StringUtils.indexOf("aabaabaa", "")); + + assertEquals(2, StringUtils.indexOf(new StringBuilder("aabaabaa"), "b")); + } + + public void testIndexOf_StringInt() { + assertEquals(-1, StringUtils.indexOf(null, null, 0)); + assertEquals(-1, StringUtils.indexOf(null, null, -1)); + assertEquals(-1, StringUtils.indexOf(null, "", 0)); + assertEquals(-1, StringUtils.indexOf(null, "", -1)); + assertEquals(-1, StringUtils.indexOf("", null, 0)); + assertEquals(-1, StringUtils.indexOf("", null, -1)); + assertEquals(0, StringUtils.indexOf("", "", 0)); + assertEquals(0, StringUtils.indexOf("", "", -1)); + assertEquals(0, StringUtils.indexOf("", "", 9)); + assertEquals(0, StringUtils.indexOf("abc", "", 0)); + assertEquals(0, StringUtils.indexOf("abc", "", -1)); + assertEquals(3, StringUtils.indexOf("abc", "", 9)); + assertEquals(3, StringUtils.indexOf("abc", "", 3)); + assertEquals(0, StringUtils.indexOf("aabaabaa", "a", 0)); + assertEquals(2, StringUtils.indexOf("aabaabaa", "b", 0)); + assertEquals(1, StringUtils.indexOf("aabaabaa", "ab", 0)); + assertEquals(5, StringUtils.indexOf("aabaabaa", "b", 3)); + assertEquals(-1, StringUtils.indexOf("aabaabaa", "b", 9)); + assertEquals(2, StringUtils.indexOf("aabaabaa", "b", -1)); + assertEquals(2,StringUtils.indexOf("aabaabaa", "", 2)); + + assertEquals(5, StringUtils.indexOf(new StringBuilder("aabaabaa"), "b", 3)); + } + + public void testIndexOfAny_StringCharArray() { + assertEquals(-1, StringUtils.indexOfAny(null, (char[]) null)); + assertEquals(-1, StringUtils.indexOfAny(null, new char[0])); + assertEquals(-1, StringUtils.indexOfAny(null, new char[] {'a','b'})); + + assertEquals(-1, StringUtils.indexOfAny("", (char[]) null)); + assertEquals(-1, StringUtils.indexOfAny("", new char[0])); + assertEquals(-1, StringUtils.indexOfAny("", new char[] {'a','b'})); + + assertEquals(-1, StringUtils.indexOfAny("zzabyycdxx", (char[]) null)); + assertEquals(-1, StringUtils.indexOfAny("zzabyycdxx", new char[0])); + assertEquals(0, StringUtils.indexOfAny("zzabyycdxx", new char[] {'z','a'})); + assertEquals(3, StringUtils.indexOfAny("zzabyycdxx", new char[] {'b','y'})); + assertEquals(-1, StringUtils.indexOfAny("ab", new char[] {'z'})); + } + + /** + * See http://java.sun.com/developer/technicalArticles/Intl/Supplementary/ + */ + public void testIndexOfAny_StringCharArrayWithSupplementaryChars() { + assertEquals(0, StringUtils.indexOfAny(CharU20000 + CharU20001, CharU20000.toCharArray())); + assertEquals(2, StringUtils.indexOfAny(CharU20000 + CharU20001, CharU20001.toCharArray())); + assertEquals(0, StringUtils.indexOfAny(CharU20000, CharU20000.toCharArray())); + assertEquals(-1, StringUtils.indexOfAny(CharU20000, CharU20001.toCharArray())); + } + + public void testIndexOfAny_StringString() { + assertEquals(-1, StringUtils.indexOfAny(null, (String) null)); + assertEquals(-1, StringUtils.indexOfAny(null, "")); + assertEquals(-1, StringUtils.indexOfAny(null, "ab")); + + assertEquals(-1, StringUtils.indexOfAny("", (String) null)); + assertEquals(-1, StringUtils.indexOfAny("", "")); + assertEquals(-1, StringUtils.indexOfAny("", "ab")); + + assertEquals(-1, StringUtils.indexOfAny("zzabyycdxx", (String) null)); + assertEquals(-1, StringUtils.indexOfAny("zzabyycdxx", "")); + assertEquals(0, StringUtils.indexOfAny("zzabyycdxx", "za")); + assertEquals(3, StringUtils.indexOfAny("zzabyycdxx", "by")); + assertEquals(-1, StringUtils.indexOfAny("ab", "z")); + } + + public void testIndexOfAny_StringStringArray() { + assertEquals(-1, StringUtils.indexOfAny(null, (String[]) null)); + assertEquals(-1, StringUtils.indexOfAny(null, FOOBAR_SUB_ARRAY)); + assertEquals(-1, StringUtils.indexOfAny(FOOBAR, (String[]) null)); + assertEquals(2, StringUtils.indexOfAny(FOOBAR, FOOBAR_SUB_ARRAY)); + assertEquals(-1, StringUtils.indexOfAny(FOOBAR, new String[0])); + assertEquals(-1, StringUtils.indexOfAny(null, new String[0])); + assertEquals(-1, StringUtils.indexOfAny("", new String[0])); + assertEquals(-1, StringUtils.indexOfAny(FOOBAR, new String[] {"llll"})); + assertEquals(0, StringUtils.indexOfAny(FOOBAR, new String[] {""})); + assertEquals(0, StringUtils.indexOfAny("", new String[] {""})); + assertEquals(-1, StringUtils.indexOfAny("", new String[] {"a"})); + assertEquals(-1, StringUtils.indexOfAny("", new String[] {null})); + assertEquals(-1, StringUtils.indexOfAny(FOOBAR, new String[] {null})); + assertEquals(-1, StringUtils.indexOfAny(null, new String[] {null})); + } + + /** + * See http://java.sun.com/developer/technicalArticles/Intl/Supplementary/ + */ + public void testIndexOfAny_StringStringWithSupplementaryChars() { + assertEquals(0, StringUtils.indexOfAny(CharU20000 + CharU20001, CharU20000)); + assertEquals(2, StringUtils.indexOfAny(CharU20000 + CharU20001, CharU20001)); + assertEquals(0, StringUtils.indexOfAny(CharU20000, CharU20000)); + assertEquals(-1, StringUtils.indexOfAny(CharU20000, CharU20001)); + } + + public void testIndexOfAnyBut_StringCharArray() { + assertEquals(-1, StringUtils.indexOfAnyBut(null, (char[]) null)); + assertEquals(-1, StringUtils.indexOfAnyBut(null, new char[0])); + assertEquals(-1, StringUtils.indexOfAnyBut(null, new char[] {'a','b'})); + + assertEquals(-1, StringUtils.indexOfAnyBut("", (char[]) null)); + assertEquals(-1, StringUtils.indexOfAnyBut("", new char[0])); + assertEquals(-1, StringUtils.indexOfAnyBut("", new char[] {'a','b'})); + + assertEquals(-1, StringUtils.indexOfAnyBut("zzabyycdxx", (char[]) null)); + assertEquals(-1, StringUtils.indexOfAnyBut("zzabyycdxx", new char[0])); + assertEquals(3, StringUtils.indexOfAnyBut("zzabyycdxx", new char[] {'z','a'})); + assertEquals(0, StringUtils.indexOfAnyBut("zzabyycdxx", new char[] {'b','y'})); + assertEquals(-1, StringUtils.indexOfAnyBut("aba", new char[] {'a', 'b'})); + assertEquals(0, StringUtils.indexOfAnyBut("aba", new char[] {'z'})); + + } + + public void testIndexOfAnyBut_StringCharArrayWithSupplementaryChars() { + assertEquals(2, StringUtils.indexOfAnyBut(CharU20000 + CharU20001, CharU20000.toCharArray())); + assertEquals(0, StringUtils.indexOfAnyBut(CharU20000 + CharU20001, CharU20001.toCharArray())); + assertEquals(-1, StringUtils.indexOfAnyBut(CharU20000, CharU20000.toCharArray())); + assertEquals(0, StringUtils.indexOfAnyBut(CharU20000, CharU20001.toCharArray())); + } + + public void testIndexOfAnyBut_StringString() { + assertEquals(-1, StringUtils.indexOfAnyBut(null, (String) null)); + assertEquals(-1, StringUtils.indexOfAnyBut(null, "")); + assertEquals(-1, StringUtils.indexOfAnyBut(null, "ab")); + + assertEquals(-1, StringUtils.indexOfAnyBut("", (String) null)); + assertEquals(-1, StringUtils.indexOfAnyBut("", "")); + assertEquals(-1, StringUtils.indexOfAnyBut("", "ab")); + + assertEquals(-1, StringUtils.indexOfAnyBut("zzabyycdxx", (String) null)); + assertEquals(-1, StringUtils.indexOfAnyBut("zzabyycdxx", "")); + assertEquals(3, StringUtils.indexOfAnyBut("zzabyycdxx", "za")); + assertEquals(0, StringUtils.indexOfAnyBut("zzabyycdxx", "by")); + assertEquals(0, StringUtils.indexOfAnyBut("ab", "z")); + } + + public void testIndexOfAnyBut_StringStringWithSupplementaryChars() { + assertEquals(2, StringUtils.indexOfAnyBut(CharU20000 + CharU20001, CharU20000)); + assertEquals(0, StringUtils.indexOfAnyBut(CharU20000 + CharU20001, CharU20001)); + assertEquals(-1, StringUtils.indexOfAnyBut(CharU20000, CharU20000)); + assertEquals(0, StringUtils.indexOfAnyBut(CharU20000, CharU20001)); + } + + public void testIndexOfIgnoreCase_String() { + assertEquals(-1, StringUtils.indexOfIgnoreCase(null, null)); + assertEquals(-1, StringUtils.indexOfIgnoreCase(null, "")); + assertEquals(-1, StringUtils.indexOfIgnoreCase("", null)); + assertEquals(0, StringUtils.indexOfIgnoreCase("", "")); + assertEquals(0, StringUtils.indexOfIgnoreCase("aabaabaa", "a")); + assertEquals(0, StringUtils.indexOfIgnoreCase("aabaabaa", "A")); + assertEquals(2, StringUtils.indexOfIgnoreCase("aabaabaa", "b")); + assertEquals(2, StringUtils.indexOfIgnoreCase("aabaabaa", "B")); + assertEquals(1, StringUtils.indexOfIgnoreCase("aabaabaa", "ab")); + assertEquals(1, StringUtils.indexOfIgnoreCase("aabaabaa", "AB")); + assertEquals(0, StringUtils.indexOfIgnoreCase("aabaabaa", "")); + } + + public void testIndexOfIgnoreCase_StringInt() { + assertEquals(1, StringUtils.indexOfIgnoreCase("aabaabaa", "AB", -1)); + assertEquals(1, StringUtils.indexOfIgnoreCase("aabaabaa", "AB", 0)); + assertEquals(1, StringUtils.indexOfIgnoreCase("aabaabaa", "AB", 1)); + assertEquals(4, StringUtils.indexOfIgnoreCase("aabaabaa", "AB", 2)); + assertEquals(4, StringUtils.indexOfIgnoreCase("aabaabaa", "AB", 3)); + assertEquals(4, StringUtils.indexOfIgnoreCase("aabaabaa", "AB", 4)); + assertEquals(-1, StringUtils.indexOfIgnoreCase("aabaabaa", "AB", 5)); + assertEquals(-1, StringUtils.indexOfIgnoreCase("aabaabaa", "AB", 6)); + assertEquals(-1, StringUtils.indexOfIgnoreCase("aabaabaa", "AB", 7)); + assertEquals(-1, StringUtils.indexOfIgnoreCase("aabaabaa", "AB", 8)); + assertEquals(1, StringUtils.indexOfIgnoreCase("aab", "AB", 1)); + assertEquals(5, StringUtils.indexOfIgnoreCase("aabaabaa", "", 5)); + assertEquals(-1, StringUtils.indexOfIgnoreCase("ab", "AAB", 0)); + assertEquals(-1, StringUtils.indexOfIgnoreCase("aab", "AAB", 1)); + } + + public void testLastIndexOf_char() { + assertEquals(-1, StringUtils.lastIndexOf(null, ' ')); + assertEquals(-1, StringUtils.lastIndexOf("", ' ')); + assertEquals(7, StringUtils.lastIndexOf("aabaabaa", 'a')); + assertEquals(5, StringUtils.lastIndexOf("aabaabaa", 'b')); + + assertEquals(5, StringUtils.lastIndexOf(new StringBuilder("aabaabaa"), 'b')); + } + + public void testLastIndexOf_charInt() { + assertEquals(-1, StringUtils.lastIndexOf(null, ' ', 0)); + assertEquals(-1, StringUtils.lastIndexOf(null, ' ', -1)); + assertEquals(-1, StringUtils.lastIndexOf("", ' ', 0)); + assertEquals(-1, StringUtils.lastIndexOf("", ' ', -1)); + assertEquals(7, StringUtils.lastIndexOf("aabaabaa", 'a', 8)); + assertEquals(5, StringUtils.lastIndexOf("aabaabaa", 'b', 8)); + assertEquals(2, StringUtils.lastIndexOf("aabaabaa", 'b', 3)); + assertEquals(5, StringUtils.lastIndexOf("aabaabaa", 'b', 9)); + assertEquals(-1, StringUtils.lastIndexOf("aabaabaa", 'b', -1)); + assertEquals(0, StringUtils.lastIndexOf("aabaabaa", 'a', 0)); + + assertEquals(2, StringUtils.lastIndexOf(new StringBuilder("aabaabaa"), 'b', 2)); + } + + public void testLastIndexOf_String() { + assertEquals(-1, StringUtils.lastIndexOf(null, null)); + assertEquals(-1, StringUtils.lastIndexOf("", null)); + assertEquals(-1, StringUtils.lastIndexOf("", "a")); + assertEquals(0, StringUtils.lastIndexOf("", "")); + assertEquals(8, StringUtils.lastIndexOf("aabaabaa", "")); + assertEquals(7, StringUtils.lastIndexOf("aabaabaa", "a")); + assertEquals(5, StringUtils.lastIndexOf("aabaabaa", "b")); + assertEquals(4, StringUtils.lastIndexOf("aabaabaa", "ab")); + + assertEquals(4, StringUtils.lastIndexOf(new StringBuilder("aabaabaa"), "ab")); + } + + public void testLastIndexOf_StringInt() { + assertEquals(-1, StringUtils.lastIndexOf(null, null, 0)); + assertEquals(-1, StringUtils.lastIndexOf(null, null, -1)); + assertEquals(-1, StringUtils.lastIndexOf(null, "", 0)); + assertEquals(-1, StringUtils.lastIndexOf(null, "", -1)); + assertEquals(-1, StringUtils.lastIndexOf("", null, 0)); + assertEquals(-1, StringUtils.lastIndexOf("", null, -1)); + assertEquals(0, StringUtils.lastIndexOf("", "", 0)); + assertEquals(-1, StringUtils.lastIndexOf("", "", -1)); + assertEquals(0, StringUtils.lastIndexOf("", "", 9)); + assertEquals(0, StringUtils.lastIndexOf("abc", "", 0)); + assertEquals(-1, StringUtils.lastIndexOf("abc", "", -1)); + assertEquals(3, StringUtils.lastIndexOf("abc", "", 9)); + assertEquals(7, StringUtils.lastIndexOf("aabaabaa", "a", 8)); + assertEquals(5, StringUtils.lastIndexOf("aabaabaa", "b", 8)); + assertEquals(4, StringUtils.lastIndexOf("aabaabaa", "ab", 8)); + assertEquals(2, StringUtils.lastIndexOf("aabaabaa", "b", 3)); + assertEquals(5, StringUtils.lastIndexOf("aabaabaa", "b", 9)); + assertEquals(-1, StringUtils.lastIndexOf("aabaabaa", "b", -1)); + assertEquals(-1, StringUtils.lastIndexOf("aabaabaa", "b", 0)); + assertEquals(0, StringUtils.lastIndexOf("aabaabaa", "a", 0)); + + assertEquals(2, StringUtils.lastIndexOf(new StringBuilder("aabaabaa"), "b", 3)); + } + + public void testLastIndexOfAny_StringStringArray() { + assertEquals(-1, StringUtils.lastIndexOfAny(null, (CharSequence) null)); // test both types of ... + assertEquals(-1, StringUtils.lastIndexOfAny(null, (CharSequence[]) null)); // ... varargs invocation + assertEquals(-1, StringUtils.lastIndexOfAny(null)); // Missing varag + assertEquals(-1, StringUtils.lastIndexOfAny(null, FOOBAR_SUB_ARRAY)); + assertEquals(-1, StringUtils.lastIndexOfAny(FOOBAR, (CharSequence) null)); // test both types of ... + assertEquals(-1, StringUtils.lastIndexOfAny(FOOBAR, (CharSequence[]) null)); // ... varargs invocation + assertEquals(-1, StringUtils.lastIndexOfAny(FOOBAR)); // Missing vararg + assertEquals(3, StringUtils.lastIndexOfAny(FOOBAR, FOOBAR_SUB_ARRAY)); + assertEquals(-1, StringUtils.lastIndexOfAny(FOOBAR, new String[0])); + assertEquals(-1, StringUtils.lastIndexOfAny(null, new String[0])); + assertEquals(-1, StringUtils.lastIndexOfAny("", new String[0])); + assertEquals(-1, StringUtils.lastIndexOfAny(FOOBAR, new String[] {"llll"})); + assertEquals(6, StringUtils.lastIndexOfAny(FOOBAR, new String[] {""})); + assertEquals(0, StringUtils.lastIndexOfAny("", new String[] {""})); + assertEquals(-1, StringUtils.lastIndexOfAny("", new String[] {"a"})); + assertEquals(-1, StringUtils.lastIndexOfAny("", new String[] {null})); + assertEquals(-1, StringUtils.lastIndexOfAny(FOOBAR, new String[] {null})); + assertEquals(-1, StringUtils.lastIndexOfAny(null, new String[] {null})); + } + + public void testLastIndexOfIgnoreCase_String() { + assertEquals(-1, StringUtils.lastIndexOfIgnoreCase(null, null)); + assertEquals(-1, StringUtils.lastIndexOfIgnoreCase("", null)); + assertEquals(-1, StringUtils.lastIndexOfIgnoreCase(null, "")); + assertEquals(-1, StringUtils.lastIndexOfIgnoreCase("", "a")); + assertEquals(0, StringUtils.lastIndexOfIgnoreCase("", "")); + assertEquals(8, StringUtils.lastIndexOfIgnoreCase("aabaabaa", "")); + assertEquals(7, StringUtils.lastIndexOfIgnoreCase("aabaabaa", "a")); + assertEquals(7, StringUtils.lastIndexOfIgnoreCase("aabaabaa", "A")); + assertEquals(5, StringUtils.lastIndexOfIgnoreCase("aabaabaa", "b")); + assertEquals(5, StringUtils.lastIndexOfIgnoreCase("aabaabaa", "B")); + assertEquals(4, StringUtils.lastIndexOfIgnoreCase("aabaabaa", "ab")); + assertEquals(4, StringUtils.lastIndexOfIgnoreCase("aabaabaa", "AB")); + assertEquals(-1, StringUtils.lastIndexOfIgnoreCase("ab", "AAB")); + assertEquals(0, StringUtils.lastIndexOfIgnoreCase("aab", "AAB")); + } + + public void testLastIndexOfIgnoreCase_StringInt() { + assertEquals(-1, StringUtils.lastIndexOfIgnoreCase(null, null, 0)); + assertEquals(-1, StringUtils.lastIndexOfIgnoreCase(null, null, -1)); + assertEquals(-1, StringUtils.lastIndexOfIgnoreCase(null, "", 0)); + assertEquals(-1, StringUtils.lastIndexOfIgnoreCase(null, "", -1)); + assertEquals(-1, StringUtils.lastIndexOfIgnoreCase("", null, 0)); + assertEquals(-1, StringUtils.lastIndexOfIgnoreCase("", null, -1)); + assertEquals(0, StringUtils.lastIndexOfIgnoreCase("", "", 0)); + assertEquals(-1, StringUtils.lastIndexOfIgnoreCase("", "", -1)); + assertEquals(0, StringUtils.lastIndexOfIgnoreCase("", "", 9)); + assertEquals(0, StringUtils.lastIndexOfIgnoreCase("abc", "", 0)); + assertEquals(-1, StringUtils.lastIndexOfIgnoreCase("abc", "", -1)); + assertEquals(3, StringUtils.lastIndexOfIgnoreCase("abc", "", 9)); + assertEquals(7, StringUtils.lastIndexOfIgnoreCase("aabaabaa", "A", 8)); + assertEquals(5, StringUtils.lastIndexOfIgnoreCase("aabaabaa", "B", 8)); + assertEquals(4, StringUtils.lastIndexOfIgnoreCase("aabaabaa", "AB", 8)); + assertEquals(2, StringUtils.lastIndexOfIgnoreCase("aabaabaa", "B", 3)); + assertEquals(5, StringUtils.lastIndexOfIgnoreCase("aabaabaa", "B", 9)); + assertEquals(-1, StringUtils.lastIndexOfIgnoreCase("aabaabaa", "B", -1)); + assertEquals(-1, StringUtils.lastIndexOfIgnoreCase("aabaabaa", "B", 0)); + assertEquals(0, StringUtils.lastIndexOfIgnoreCase("aabaabaa", "A", 0)); + assertEquals(1, StringUtils.lastIndexOfIgnoreCase("aab", "AB", 1)); + } + + public void testLastOrdinalIndexOf() { + assertEquals(-1, StringUtils.lastOrdinalIndexOf(null, "*", 42) ); + assertEquals(-1, StringUtils.lastOrdinalIndexOf("*", null, 42) ); + assertEquals(0, StringUtils.lastOrdinalIndexOf("", "", 42) ); + assertEquals(7, StringUtils.lastOrdinalIndexOf("aabaabaa", "a", 1) ); + assertEquals(6, StringUtils.lastOrdinalIndexOf("aabaabaa", "a", 2) ); + assertEquals(5, StringUtils.lastOrdinalIndexOf("aabaabaa", "b", 1) ); + assertEquals(2, StringUtils.lastOrdinalIndexOf("aabaabaa", "b", 2) ); + assertEquals(4, StringUtils.lastOrdinalIndexOf("aabaabaa", "ab", 1) ); + assertEquals(1, StringUtils.lastOrdinalIndexOf("aabaabaa", "ab", 2) ); + assertEquals(8, StringUtils.lastOrdinalIndexOf("aabaabaa", "", 1) ); + assertEquals(8, StringUtils.lastOrdinalIndexOf("aabaabaa", "", 2) ); + } + + public void testOrdinalIndexOf() { + assertEquals(-1, StringUtils.ordinalIndexOf(null, null, Integer.MIN_VALUE)); + assertEquals(-1, StringUtils.ordinalIndexOf("", null, Integer.MIN_VALUE)); + assertEquals(-1, StringUtils.ordinalIndexOf("", "", Integer.MIN_VALUE)); + assertEquals(-1, StringUtils.ordinalIndexOf("aabaabaa", "a", Integer.MIN_VALUE)); + assertEquals(-1, StringUtils.ordinalIndexOf("aabaabaa", "b", Integer.MIN_VALUE)); + assertEquals(-1, StringUtils.ordinalIndexOf("aabaabaa", "ab", Integer.MIN_VALUE)); + assertEquals(-1, StringUtils.ordinalIndexOf("aabaabaa", "", Integer.MIN_VALUE)); + + assertEquals(-1, StringUtils.ordinalIndexOf(null, null, -1)); + assertEquals(-1, StringUtils.ordinalIndexOf("", null, -1)); + assertEquals(-1, StringUtils.ordinalIndexOf("", "", -1)); + assertEquals(-1, StringUtils.ordinalIndexOf("aabaabaa", "a", -1)); + assertEquals(-1, StringUtils.ordinalIndexOf("aabaabaa", "b", -1)); + assertEquals(-1, StringUtils.ordinalIndexOf("aabaabaa", "ab", -1)); + assertEquals(-1, StringUtils.ordinalIndexOf("aabaabaa", "", -1)); + + assertEquals(-1, StringUtils.ordinalIndexOf(null, null, 0)); + assertEquals(-1, StringUtils.ordinalIndexOf("", null, 0)); + assertEquals(-1, StringUtils.ordinalIndexOf("", "", 0)); + assertEquals(-1, StringUtils.ordinalIndexOf("aabaabaa", "a", 0)); + assertEquals(-1, StringUtils.ordinalIndexOf("aabaabaa", "b", 0)); + assertEquals(-1, StringUtils.ordinalIndexOf("aabaabaa", "ab", 0)); + assertEquals(-1, StringUtils.ordinalIndexOf("aabaabaa", "", 0)); + + assertEquals(-1, StringUtils.ordinalIndexOf(null, null, 1)); + assertEquals(-1, StringUtils.ordinalIndexOf("", null, 1)); + assertEquals(0, StringUtils.ordinalIndexOf("", "", 1)); + assertEquals(0, StringUtils.ordinalIndexOf("aabaabaa", "a", 1)); + assertEquals(2, StringUtils.ordinalIndexOf("aabaabaa", "b", 1)); + assertEquals(1, StringUtils.ordinalIndexOf("aabaabaa", "ab", 1)); + assertEquals(0, StringUtils.ordinalIndexOf("aabaabaa", "", 1)); + + assertEquals(-1, StringUtils.ordinalIndexOf(null, null, 2)); + assertEquals(-1, StringUtils.ordinalIndexOf("", null, 2)); + assertEquals(0, StringUtils.ordinalIndexOf("", "", 2)); + assertEquals(1, StringUtils.ordinalIndexOf("aabaabaa", "a", 2)); + assertEquals(5, StringUtils.ordinalIndexOf("aabaabaa", "b", 2)); + assertEquals(4, StringUtils.ordinalIndexOf("aabaabaa", "ab", 2)); + assertEquals(0, StringUtils.ordinalIndexOf("aabaabaa", "", 2)); + + assertEquals(-1, StringUtils.ordinalIndexOf(null, null, Integer.MAX_VALUE)); + assertEquals(-1, StringUtils.ordinalIndexOf("", null, Integer.MAX_VALUE)); + assertEquals(0, StringUtils.ordinalIndexOf("", "", Integer.MAX_VALUE)); + assertEquals(-1, StringUtils.ordinalIndexOf("aabaabaa", "a", Integer.MAX_VALUE)); + assertEquals(-1, StringUtils.ordinalIndexOf("aabaabaa", "b", Integer.MAX_VALUE)); + assertEquals(-1, StringUtils.ordinalIndexOf("aabaabaa", "ab", Integer.MAX_VALUE)); + assertEquals(0, StringUtils.ordinalIndexOf("aabaabaa", "", Integer.MAX_VALUE)); + + assertEquals(-1, StringUtils.ordinalIndexOf("aaaaaaaaa", "a", 0)); + assertEquals(0, StringUtils.ordinalIndexOf("aaaaaaaaa", "a", 1)); + assertEquals(1, StringUtils.ordinalIndexOf("aaaaaaaaa", "a", 2)); + assertEquals(2, StringUtils.ordinalIndexOf("aaaaaaaaa", "a", 3)); + assertEquals(3, StringUtils.ordinalIndexOf("aaaaaaaaa", "a", 4)); + assertEquals(4, StringUtils.ordinalIndexOf("aaaaaaaaa", "a", 5)); + assertEquals(5, StringUtils.ordinalIndexOf("aaaaaaaaa", "a", 6)); + assertEquals(6, StringUtils.ordinalIndexOf("aaaaaaaaa", "a", 7)); + assertEquals(7, StringUtils.ordinalIndexOf("aaaaaaaaa", "a", 8)); + assertEquals(8, StringUtils.ordinalIndexOf("aaaaaaaaa", "a", 9)); + assertEquals(-1, StringUtils.ordinalIndexOf("aaaaaaaaa", "a", 10)); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/StringUtilsIsTest.java b/ApacheCommonsLang/org/apache/commons/lang3/StringUtilsIsTest.java new file mode 100644 index 0000000..f8ab656 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/StringUtilsIsTest.java @@ -0,0 +1,164 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3; + +import junit.framework.TestCase; + +/** + * Unit tests {@link org.apache.commons.lang3.StringUtils} - Substring methods + * + * @version $Id: StringUtilsIsTest.java 1088899 2011-04-05 05:31:27Z bayard $ + */ +public class StringUtilsIsTest extends TestCase { + + public StringUtilsIsTest(String name) { + super(name); + } + + //----------------------------------------------------------------------- + + public void testIsAlpha() { + assertEquals(false, StringUtils.isAlpha(null)); + assertEquals(false, StringUtils.isAlpha("")); + assertEquals(false, StringUtils.isAlpha(" ")); + assertEquals(true, StringUtils.isAlpha("a")); + assertEquals(true, StringUtils.isAlpha("A")); + assertEquals(true, StringUtils.isAlpha("kgKgKgKgkgkGkjkjlJlOKLgHdGdHgl")); + assertEquals(false, StringUtils.isAlpha("ham kso")); + assertEquals(false, StringUtils.isAlpha("1")); + assertEquals(false, StringUtils.isAlpha("hkHKHik6iUGHKJgU7tUJgKJGI87GIkug")); + assertEquals(false, StringUtils.isAlpha("_")); + assertEquals(false, StringUtils.isAlpha("hkHKHik*khbkuh")); + } + + public void testIsAlphanumeric() { + assertEquals(false, StringUtils.isAlphanumeric(null)); + assertEquals(false, StringUtils.isAlphanumeric("")); + assertEquals(false, StringUtils.isAlphanumeric(" ")); + assertEquals(true, StringUtils.isAlphanumeric("a")); + assertEquals(true, StringUtils.isAlphanumeric("A")); + assertEquals(true, StringUtils.isAlphanumeric("kgKgKgKgkgkGkjkjlJlOKLgHdGdHgl")); + assertEquals(false, StringUtils.isAlphanumeric("ham kso")); + assertEquals(true, StringUtils.isAlphanumeric("1")); + assertEquals(true, StringUtils.isAlphanumeric("hkHKHik6iUGHKJgU7tUJgKJGI87GIkug")); + assertEquals(false, StringUtils.isAlphanumeric("_")); + assertEquals(false, StringUtils.isAlphanumeric("hkHKHik*khbkuh")); + } + + public void testIsWhitespace() { + assertEquals(false, StringUtils.isWhitespace(null)); + assertEquals(true, StringUtils.isWhitespace("")); + assertEquals(true, StringUtils.isWhitespace(" ")); + assertEquals(true, StringUtils.isWhitespace("\t \n \t")); + assertEquals(false, StringUtils.isWhitespace("\t aa\n \t")); + assertEquals(true, StringUtils.isWhitespace(" ")); + assertEquals(false, StringUtils.isWhitespace(" a ")); + assertEquals(false, StringUtils.isWhitespace("a ")); + assertEquals(false, StringUtils.isWhitespace(" a")); + assertEquals(false, StringUtils.isWhitespace("aba")); + assertEquals(true, StringUtils.isWhitespace(StringUtilsTest.WHITESPACE)); + assertEquals(false, StringUtils.isWhitespace(StringUtilsTest.NON_WHITESPACE)); + } + + public void testIsAlphaspace() { + assertEquals(false, StringUtils.isAlphaSpace(null)); + assertEquals(true, StringUtils.isAlphaSpace("")); + assertEquals(true, StringUtils.isAlphaSpace(" ")); + assertEquals(true, StringUtils.isAlphaSpace("a")); + assertEquals(true, StringUtils.isAlphaSpace("A")); + assertEquals(true, StringUtils.isAlphaSpace("kgKgKgKgkgkGkjkjlJlOKLgHdGdHgl")); + assertEquals(true, StringUtils.isAlphaSpace("ham kso")); + assertEquals(false, StringUtils.isAlphaSpace("1")); + assertEquals(false, StringUtils.isAlphaSpace("hkHKHik6iUGHKJgU7tUJgKJGI87GIkug")); + assertEquals(false, StringUtils.isAlphaSpace("_")); + assertEquals(false, StringUtils.isAlphaSpace("hkHKHik*khbkuh")); + } + + public void testIsAlphanumericSpace() { + assertEquals(false, StringUtils.isAlphanumericSpace(null)); + assertEquals(true, StringUtils.isAlphanumericSpace("")); + assertEquals(true, StringUtils.isAlphanumericSpace(" ")); + assertEquals(true, StringUtils.isAlphanumericSpace("a")); + assertEquals(true, StringUtils.isAlphanumericSpace("A")); + assertEquals(true, StringUtils.isAlphanumericSpace("kgKgKgKgkgkGkjkjlJlOKLgHdGdHgl")); + assertEquals(true, StringUtils.isAlphanumericSpace("ham kso")); + assertEquals(true, StringUtils.isAlphanumericSpace("1")); + assertEquals(true, StringUtils.isAlphanumericSpace("hkHKHik6iUGHKJgU7tUJgKJGI87GIkug")); + assertEquals(false, StringUtils.isAlphanumericSpace("_")); + assertEquals(false, StringUtils.isAlphanumericSpace("hkHKHik*khbkuh")); + } + + public void testIsAsciiPrintable_String() { + assertEquals(false, StringUtils.isAsciiPrintable(null)); + assertEquals(true, StringUtils.isAsciiPrintable("")); + assertEquals(true, StringUtils.isAsciiPrintable(" ")); + assertEquals(true, StringUtils.isAsciiPrintable("a")); + assertEquals(true, StringUtils.isAsciiPrintable("A")); + assertEquals(true, StringUtils.isAsciiPrintable("1")); + assertEquals(true, StringUtils.isAsciiPrintable("Ceki")); + assertEquals(true, StringUtils.isAsciiPrintable("!ab2c~")); + assertEquals(true, StringUtils.isAsciiPrintable("1000")); + assertEquals(true, StringUtils.isAsciiPrintable("10 00")); + assertEquals(false, StringUtils.isAsciiPrintable("10\t00")); + assertEquals(true, StringUtils.isAsciiPrintable("10.00")); + assertEquals(true, StringUtils.isAsciiPrintable("10,00")); + assertEquals(true, StringUtils.isAsciiPrintable("!ab-c~")); + assertEquals(true, StringUtils.isAsciiPrintable("hkHK=Hik6i?UGH_KJgU7.tUJgKJ*GI87GI,kug")); + assertEquals(true, StringUtils.isAsciiPrintable("\u0020")); + assertEquals(true, StringUtils.isAsciiPrintable("\u0021")); + assertEquals(true, StringUtils.isAsciiPrintable("\u007e")); + assertEquals(false, StringUtils.isAsciiPrintable("\u007f")); + assertEquals(true, StringUtils.isAsciiPrintable("G?lc?")); + assertEquals(true, StringUtils.isAsciiPrintable("=?iso-8859-1?Q?G=FClc=FC?=")); + assertEquals(false, StringUtils.isAsciiPrintable("G\u00fclc\u00fc")); + } + + public void testIsNumeric() { + assertEquals(false, StringUtils.isNumeric(null)); + assertEquals(false, StringUtils.isNumeric("")); + assertEquals(false, StringUtils.isNumeric(" ")); + assertEquals(false, StringUtils.isNumeric("a")); + assertEquals(false, StringUtils.isNumeric("A")); + assertEquals(false, StringUtils.isNumeric("kgKgKgKgkgkGkjkjlJlOKLgHdGdHgl")); + assertEquals(false, StringUtils.isNumeric("ham kso")); + assertEquals(true, StringUtils.isNumeric("1")); + assertEquals(true, StringUtils.isNumeric("1000")); + assertEquals(false, StringUtils.isNumeric("2.3")); + assertEquals(false, StringUtils.isNumeric("10 00")); + assertEquals(false, StringUtils.isNumeric("hkHKHik6iUGHKJgU7tUJgKJGI87GIkug")); + assertEquals(false, StringUtils.isNumeric("_")); + assertEquals(false, StringUtils.isNumeric("hkHKHik*khbkuh")); + } + + public void testIsNumericSpace() { + assertEquals(false, StringUtils.isNumericSpace(null)); + assertEquals(true, StringUtils.isNumericSpace("")); + assertEquals(true, StringUtils.isNumericSpace(" ")); + assertEquals(false, StringUtils.isNumericSpace("a")); + assertEquals(false, StringUtils.isNumericSpace("A")); + assertEquals(false, StringUtils.isNumericSpace("kgKgKgKgkgkGkjkjlJlOKLgHdGdHgl")); + assertEquals(false, StringUtils.isNumericSpace("ham kso")); + assertEquals(true, StringUtils.isNumericSpace("1")); + assertEquals(true, StringUtils.isNumericSpace("1000")); + assertEquals(false, StringUtils.isNumericSpace("2.3")); + assertEquals(true, StringUtils.isNumericSpace("10 00")); + assertEquals(false, StringUtils.isNumericSpace("hkHKHik6iUGHKJgU7tUJgKJGI87GIkug")); + assertEquals(false, StringUtils.isNumericSpace("_")); + assertEquals(false, StringUtils.isNumericSpace("hkHKHik*khbkuh")); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/StringUtilsStartsEndsWithTest.java b/ApacheCommonsLang/org/apache/commons/lang3/StringUtilsStartsEndsWithTest.java new file mode 100644 index 0000000..e12e767 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/StringUtilsStartsEndsWithTest.java @@ -0,0 +1,167 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3; + +import junit.framework.TestCase; + +import org.apache.commons.lang3.text.StrBuilder; + +/** + * Unit tests {@link org.apache.commons.lang3.StringUtils} - StartsWith/EndsWith methods + * + * @version $Id: StringUtilsStartsEndsWithTest.java 1144929 2011-07-10 18:26:16Z ggregory $ + */ +public class StringUtilsStartsEndsWithTest extends TestCase { + private static final String foo = "foo"; + private static final String bar = "bar"; + private static final String foobar = "foobar"; + private static final String FOO = "FOO"; + private static final String BAR = "BAR"; + private static final String FOOBAR = "FOOBAR"; + + public StringUtilsStartsEndsWithTest(String name) { + super(name); + } + + //----------------------------------------------------------------------- + + /** + * Test StringUtils.startsWith() + */ + public void testStartsWith() { + assertTrue("startsWith(null, null)", StringUtils.startsWith(null, (String)null)); + assertFalse("startsWith(FOOBAR, null)", StringUtils.startsWith(FOOBAR, (String)null)); + assertFalse("startsWith(null, FOO)", StringUtils.startsWith(null, FOO)); + assertTrue("startsWith(FOOBAR, \"\")", StringUtils.startsWith(FOOBAR, "")); + + assertTrue("startsWith(foobar, foo)", StringUtils.startsWith(foobar, foo)); + assertTrue("startsWith(FOOBAR, FOO)", StringUtils.startsWith(FOOBAR, FOO)); + assertFalse("startsWith(foobar, FOO)", StringUtils.startsWith(foobar, FOO)); + assertFalse("startsWith(FOOBAR, foo)", StringUtils.startsWith(FOOBAR, foo)); + + assertFalse("startsWith(foo, foobar)", StringUtils.startsWith(foo, foobar)); + assertFalse("startsWith(foo, foobar)", StringUtils.startsWith(bar, foobar)); + + assertFalse("startsWith(foobar, bar)", StringUtils.startsWith(foobar, bar)); + assertFalse("startsWith(FOOBAR, BAR)", StringUtils.startsWith(FOOBAR, BAR)); + assertFalse("startsWith(foobar, BAR)", StringUtils.startsWith(foobar, BAR)); + assertFalse("startsWith(FOOBAR, bar)", StringUtils.startsWith(FOOBAR, bar)); + } + + /** + * Test StringUtils.testStartsWithIgnoreCase() + */ + public void testStartsWithIgnoreCase() { + assertTrue("startsWithIgnoreCase(null, null)", StringUtils.startsWithIgnoreCase(null, (String)null)); + assertFalse("startsWithIgnoreCase(FOOBAR, null)", StringUtils.startsWithIgnoreCase(FOOBAR, (String)null)); + assertFalse("startsWithIgnoreCase(null, FOO)", StringUtils.startsWithIgnoreCase(null, FOO)); + assertTrue("startsWithIgnoreCase(FOOBAR, \"\")", StringUtils.startsWithIgnoreCase(FOOBAR, "")); + + assertTrue("startsWithIgnoreCase(foobar, foo)", StringUtils.startsWithIgnoreCase(foobar, foo)); + assertTrue("startsWithIgnoreCase(FOOBAR, FOO)", StringUtils.startsWithIgnoreCase(FOOBAR, FOO)); + assertTrue("startsWithIgnoreCase(foobar, FOO)", StringUtils.startsWithIgnoreCase(foobar, FOO)); + assertTrue("startsWithIgnoreCase(FOOBAR, foo)", StringUtils.startsWithIgnoreCase(FOOBAR, foo)); + + assertFalse("startsWithIgnoreCase(foo, foobar)", StringUtils.startsWithIgnoreCase(foo, foobar)); + assertFalse("startsWithIgnoreCase(foo, foobar)", StringUtils.startsWithIgnoreCase(bar, foobar)); + + assertFalse("startsWithIgnoreCase(foobar, bar)", StringUtils.startsWithIgnoreCase(foobar, bar)); + assertFalse("startsWithIgnoreCase(FOOBAR, BAR)", StringUtils.startsWithIgnoreCase(FOOBAR, BAR)); + assertFalse("startsWithIgnoreCase(foobar, BAR)", StringUtils.startsWithIgnoreCase(foobar, BAR)); + assertFalse("startsWithIgnoreCase(FOOBAR, bar)", StringUtils.startsWithIgnoreCase(FOOBAR, bar)); + } + + public void testStartsWithAny() { + assertFalse(StringUtils.startsWithAny(null, (String[])null)); + assertFalse(StringUtils.startsWithAny(null, "abc")); + assertFalse(StringUtils.startsWithAny("abcxyz", (String[])null)); + assertFalse(StringUtils.startsWithAny("abcxyz")); + assertTrue(StringUtils.startsWithAny("abcxyz", "abc")); + assertTrue(StringUtils.startsWithAny("abcxyz", null, "xyz", "abc")); + assertFalse(StringUtils.startsWithAny("abcxyz", null, "xyz", "abcd")); + + assertTrue("StringUtils.startsWithAny(abcxyz, StringBuilder(xyz), StringBuffer(abc))", StringUtils.startsWithAny("abcxyz", new StringBuilder("xyz"), new StringBuffer("abc"))); + assertTrue("StringUtils.startsWithAny( StrBuilder(abcxyz), StringBuilder(xyz), StringBuffer(abc))", StringUtils.startsWithAny( new StrBuilder("abcxyz"), new StringBuilder("xyz"), new StringBuffer("abc"))); + } + + + /** + * Test StringUtils.endsWith() + */ + public void testEndsWith() { + assertTrue("endsWith(null, null)", StringUtils.endsWith(null, (String)null)); + assertFalse("endsWith(FOOBAR, null)", StringUtils.endsWith(FOOBAR, (String)null)); + assertFalse("endsWith(null, FOO)", StringUtils.endsWith(null, FOO)); + assertTrue("endsWith(FOOBAR, \"\")", StringUtils.endsWith(FOOBAR, "")); + + assertFalse("endsWith(foobar, foo)", StringUtils.endsWith(foobar, foo)); + assertFalse("endsWith(FOOBAR, FOO)", StringUtils.endsWith(FOOBAR, FOO)); + assertFalse("endsWith(foobar, FOO)", StringUtils.endsWith(foobar, FOO)); + assertFalse("endsWith(FOOBAR, foo)", StringUtils.endsWith(FOOBAR, foo)); + + assertFalse("endsWith(foo, foobar)", StringUtils.endsWith(foo, foobar)); + assertFalse("endsWith(foo, foobar)", StringUtils.endsWith(bar, foobar)); + + assertTrue("endsWith(foobar, bar)", StringUtils.endsWith(foobar, bar)); + assertTrue("endsWith(FOOBAR, BAR)", StringUtils.endsWith(FOOBAR, BAR)); + assertFalse("endsWith(foobar, BAR)", StringUtils.endsWith(foobar, BAR)); + assertFalse("endsWith(FOOBAR, bar)", StringUtils.endsWith(FOOBAR, bar)); + } + + /** + * Test StringUtils.endsWithIgnoreCase() + */ + public void testEndsWithIgnoreCase() { + assertTrue("endsWithIgnoreCase(null, null)", StringUtils.endsWithIgnoreCase(null, (String)null)); + assertFalse("endsWithIgnoreCase(FOOBAR, null)", StringUtils.endsWithIgnoreCase(FOOBAR, (String)null)); + assertFalse("endsWithIgnoreCase(null, FOO)", StringUtils.endsWithIgnoreCase(null, FOO)); + assertTrue("endsWithIgnoreCase(FOOBAR, \"\")", StringUtils.endsWithIgnoreCase(FOOBAR, "")); + + assertFalse("endsWithIgnoreCase(foobar, foo)", StringUtils.endsWithIgnoreCase(foobar, foo)); + assertFalse("endsWithIgnoreCase(FOOBAR, FOO)", StringUtils.endsWithIgnoreCase(FOOBAR, FOO)); + assertFalse("endsWithIgnoreCase(foobar, FOO)", StringUtils.endsWithIgnoreCase(foobar, FOO)); + assertFalse("endsWithIgnoreCase(FOOBAR, foo)", StringUtils.endsWithIgnoreCase(FOOBAR, foo)); + + assertFalse("endsWithIgnoreCase(foo, foobar)", StringUtils.endsWithIgnoreCase(foo, foobar)); + assertFalse("endsWithIgnoreCase(foo, foobar)", StringUtils.endsWithIgnoreCase(bar, foobar)); + + assertTrue("endsWithIgnoreCase(foobar, bar)", StringUtils.endsWithIgnoreCase(foobar, bar)); + assertTrue("endsWithIgnoreCase(FOOBAR, BAR)", StringUtils.endsWithIgnoreCase(FOOBAR, BAR)); + assertTrue("endsWithIgnoreCase(foobar, BAR)", StringUtils.endsWithIgnoreCase(foobar, BAR)); + assertTrue("endsWithIgnoreCase(FOOBAR, bar)", StringUtils.endsWithIgnoreCase(FOOBAR, bar)); + + // javadoc + assertTrue(StringUtils.endsWithIgnoreCase("abcdef", "def")); + assertTrue(StringUtils.endsWithIgnoreCase("ABCDEF", "def")); + assertFalse(StringUtils.endsWithIgnoreCase("ABCDEF", "cde")); + } + + public void testEndsWithAny() { + assertFalse("StringUtils.endsWithAny(null, null)", StringUtils.endsWithAny(null, (String)null)); + assertFalse("StringUtils.endsWithAny(null, new String[] {abc})", StringUtils.endsWithAny(null, new String[] {"abc"})); + assertFalse("StringUtils.endsWithAny(abcxyz, null)", StringUtils.endsWithAny("abcxyz", (String)null)); + assertTrue("StringUtils.endsWithAny(abcxyz, new String[] {\"\"})", StringUtils.endsWithAny("abcxyz", new String[] {""})); + assertTrue("StringUtils.endsWithAny(abcxyz, new String[] {xyz})", StringUtils.endsWithAny("abcxyz", new String[] {"xyz"})); + assertTrue("StringUtils.endsWithAny(abcxyz, new String[] {null, xyz, abc})", StringUtils.endsWithAny("abcxyz", new String[] {null, "xyz", "abc"})); + assertFalse("StringUtils.endsWithAny(defg, new String[] {null, xyz, abc})", StringUtils.endsWithAny("defg", new String[] {null, "xyz", "abc"})); + + assertTrue("StringUtils.endsWithAny(abcxyz, StringBuilder(abc), StringBuffer(xyz))", StringUtils.endsWithAny("abcxyz", new StringBuilder("abc"), new StringBuffer("xyz"))); + assertTrue("StringUtils.endsWithAny( StrBuilder(abcxyz), StringBuilder(abc), StringBuffer(xyz))", StringUtils.endsWithAny( new StrBuilder("abcxyz"), new StringBuilder("abc"), new StringBuffer("xyz"))); + } + + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/StringUtilsSubstringTest.java b/ApacheCommonsLang/org/apache/commons/lang3/StringUtilsSubstringTest.java new file mode 100644 index 0000000..0cc43c3 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/StringUtilsSubstringTest.java @@ -0,0 +1,313 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3; + +import junit.framework.TestCase; + +/** + * Unit tests {@link org.apache.commons.lang3.StringUtils} - Substring methods + * + * @version $Id: StringUtilsSubstringTest.java 1089970 2011-04-07 20:05:50Z sebb $ + */ +public class StringUtilsSubstringTest extends TestCase { + private static final String FOO = "foo"; + private static final String BAR = "bar"; + private static final String BAZ = "baz"; + private static final String FOOBAR = "foobar"; + private static final String SENTENCE = "foo bar baz"; + + public StringUtilsSubstringTest(String name) { + super(name); + } + + //----------------------------------------------------------------------- + + + public void testSubstring_StringInt() { + assertEquals(null, StringUtils.substring(null, 0)); + assertEquals("", StringUtils.substring("", 0)); + assertEquals("", StringUtils.substring("", 2)); + + assertEquals("", StringUtils.substring(SENTENCE, 80)); + assertEquals(BAZ, StringUtils.substring(SENTENCE, 8)); + assertEquals(BAZ, StringUtils.substring(SENTENCE, -3)); + assertEquals(SENTENCE, StringUtils.substring(SENTENCE, 0)); + assertEquals("abc", StringUtils.substring("abc", -4)); + assertEquals("abc", StringUtils.substring("abc", -3)); + assertEquals("bc", StringUtils.substring("abc", -2)); + assertEquals("c", StringUtils.substring("abc", -1)); + assertEquals("abc", StringUtils.substring("abc", 0)); + assertEquals("bc", StringUtils.substring("abc", 1)); + assertEquals("c", StringUtils.substring("abc", 2)); + assertEquals("", StringUtils.substring("abc", 3)); + assertEquals("", StringUtils.substring("abc", 4)); + } + + public void testSubstring_StringIntInt() { + assertEquals(null, StringUtils.substring(null, 0, 0)); + assertEquals(null, StringUtils.substring(null, 1, 2)); + assertEquals("", StringUtils.substring("", 0, 0)); + assertEquals("", StringUtils.substring("", 1, 2)); + assertEquals("", StringUtils.substring("", -2, -1)); + + assertEquals("", StringUtils.substring(SENTENCE, 8, 6)); + assertEquals(FOO, StringUtils.substring(SENTENCE, 0, 3)); + assertEquals("o", StringUtils.substring(SENTENCE, -9, 3)); + assertEquals(FOO, StringUtils.substring(SENTENCE, 0, -8)); + assertEquals("o", StringUtils.substring(SENTENCE, -9, -8)); + assertEquals(SENTENCE, StringUtils.substring(SENTENCE, 0, 80)); + assertEquals("", StringUtils.substring(SENTENCE, 2, 2)); + assertEquals("b",StringUtils.substring("abc", -2, -1)); + } + + public void testLeft_String() { + assertSame(null, StringUtils.left(null, -1)); + assertSame(null, StringUtils.left(null, 0)); + assertSame(null, StringUtils.left(null, 2)); + + assertEquals("", StringUtils.left("", -1)); + assertEquals("", StringUtils.left("", 0)); + assertEquals("", StringUtils.left("", 2)); + + assertEquals("", StringUtils.left(FOOBAR, -1)); + assertEquals("", StringUtils.left(FOOBAR, 0)); + assertEquals(FOO, StringUtils.left(FOOBAR, 3)); + assertSame(FOOBAR, StringUtils.left(FOOBAR, 80)); + } + + public void testRight_String() { + assertSame(null, StringUtils.right(null, -1)); + assertSame(null, StringUtils.right(null, 0)); + assertSame(null, StringUtils.right(null, 2)); + + assertEquals("", StringUtils.right("", -1)); + assertEquals("", StringUtils.right("", 0)); + assertEquals("", StringUtils.right("", 2)); + + assertEquals("", StringUtils.right(FOOBAR, -1)); + assertEquals("", StringUtils.right(FOOBAR, 0)); + assertEquals(BAR, StringUtils.right(FOOBAR, 3)); + assertSame(FOOBAR, StringUtils.right(FOOBAR, 80)); + } + + public void testMid_String() { + assertSame(null, StringUtils.mid(null, -1, 0)); + assertSame(null, StringUtils.mid(null, 0, -1)); + assertSame(null, StringUtils.mid(null, 3, 0)); + assertSame(null, StringUtils.mid(null, 3, 2)); + + assertEquals("", StringUtils.mid("", 0, -1)); + assertEquals("", StringUtils.mid("", 0, 0)); + assertEquals("", StringUtils.mid("", 0, 2)); + + assertEquals("", StringUtils.mid(FOOBAR, 3, -1)); + assertEquals("", StringUtils.mid(FOOBAR, 3, 0)); + assertEquals("b", StringUtils.mid(FOOBAR, 3, 1)); + assertEquals(FOO, StringUtils.mid(FOOBAR, 0, 3)); + assertEquals(BAR, StringUtils.mid(FOOBAR, 3, 3)); + assertEquals(FOOBAR, StringUtils.mid(FOOBAR, 0, 80)); + assertEquals(BAR, StringUtils.mid(FOOBAR, 3, 80)); + assertEquals("", StringUtils.mid(FOOBAR, 9, 3)); + assertEquals(FOO, StringUtils.mid(FOOBAR, -1, 3)); + } + + //----------------------------------------------------------------------- + public void testSubstringBefore_StringString() { + assertEquals("foo", StringUtils.substringBefore("fooXXbarXXbaz", "XX")); + + assertEquals(null, StringUtils.substringBefore(null, null)); + assertEquals(null, StringUtils.substringBefore(null, "")); + assertEquals(null, StringUtils.substringBefore(null, "XX")); + assertEquals("", StringUtils.substringBefore("", null)); + assertEquals("", StringUtils.substringBefore("", "")); + assertEquals("", StringUtils.substringBefore("", "XX")); + + assertEquals("foo", StringUtils.substringBefore("foo", null)); + assertEquals("foo", StringUtils.substringBefore("foo", "b")); + assertEquals("f", StringUtils.substringBefore("foot", "o")); + assertEquals("", StringUtils.substringBefore("abc", "a")); + assertEquals("a", StringUtils.substringBefore("abcba", "b")); + assertEquals("ab", StringUtils.substringBefore("abc", "c")); + assertEquals("", StringUtils.substringBefore("abc", "")); + } + + public void testSubstringAfter_StringString() { + assertEquals("barXXbaz", StringUtils.substringAfter("fooXXbarXXbaz", "XX")); + + assertEquals(null, StringUtils.substringAfter(null, null)); + assertEquals(null, StringUtils.substringAfter(null, "")); + assertEquals(null, StringUtils.substringAfter(null, "XX")); + assertEquals("", StringUtils.substringAfter("", null)); + assertEquals("", StringUtils.substringAfter("", "")); + assertEquals("", StringUtils.substringAfter("", "XX")); + + assertEquals("", StringUtils.substringAfter("foo", null)); + assertEquals("ot", StringUtils.substringAfter("foot", "o")); + assertEquals("bc", StringUtils.substringAfter("abc", "a")); + assertEquals("cba", StringUtils.substringAfter("abcba", "b")); + assertEquals("", StringUtils.substringAfter("abc", "c")); + assertEquals("abc", StringUtils.substringAfter("abc", "")); + assertEquals("", StringUtils.substringAfter("abc", "d")); + } + + public void testSubstringBeforeLast_StringString() { + assertEquals("fooXXbar", StringUtils.substringBeforeLast("fooXXbarXXbaz", "XX")); + + assertEquals(null, StringUtils.substringBeforeLast(null, null)); + assertEquals(null, StringUtils.substringBeforeLast(null, "")); + assertEquals(null, StringUtils.substringBeforeLast(null, "XX")); + assertEquals("", StringUtils.substringBeforeLast("", null)); + assertEquals("", StringUtils.substringBeforeLast("", "")); + assertEquals("", StringUtils.substringBeforeLast("", "XX")); + + assertEquals("foo", StringUtils.substringBeforeLast("foo", null)); + assertEquals("foo", StringUtils.substringBeforeLast("foo", "b")); + assertEquals("fo", StringUtils.substringBeforeLast("foo", "o")); + assertEquals("abc\r\n", StringUtils.substringBeforeLast("abc\r\n", "d")); + assertEquals("abc", StringUtils.substringBeforeLast("abcdabc", "d")); + assertEquals("abcdabc", StringUtils.substringBeforeLast("abcdabcd", "d")); + assertEquals("a", StringUtils.substringBeforeLast("abc", "b")); + assertEquals("abc ", StringUtils.substringBeforeLast("abc \n", "\n")); + assertEquals("a", StringUtils.substringBeforeLast("a", null)); + assertEquals("a", StringUtils.substringBeforeLast("a", "")); + assertEquals("", StringUtils.substringBeforeLast("a", "a")); + } + + public void testSubstringAfterLast_StringString() { + assertEquals("baz", StringUtils.substringAfterLast("fooXXbarXXbaz", "XX")); + + assertEquals(null, StringUtils.substringAfterLast(null, null)); + assertEquals(null, StringUtils.substringAfterLast(null, "")); + assertEquals(null, StringUtils.substringAfterLast(null, "XX")); + assertEquals("", StringUtils.substringAfterLast("", null)); + assertEquals("", StringUtils.substringAfterLast("", "")); + assertEquals("", StringUtils.substringAfterLast("", "a")); + + assertEquals("", StringUtils.substringAfterLast("foo", null)); + assertEquals("", StringUtils.substringAfterLast("foo", "b")); + assertEquals("t", StringUtils.substringAfterLast("foot", "o")); + assertEquals("bc", StringUtils.substringAfterLast("abc", "a")); + assertEquals("a", StringUtils.substringAfterLast("abcba", "b")); + assertEquals("", StringUtils.substringAfterLast("abc", "c")); + assertEquals("", StringUtils.substringAfterLast("", "d")); + assertEquals("", StringUtils.substringAfterLast("abc", "")); + } + + //----------------------------------------------------------------------- + public void testSubstringBetween_StringString() { + assertEquals(null, StringUtils.substringBetween(null, "tag")); + assertEquals("", StringUtils.substringBetween("", "")); + assertEquals(null, StringUtils.substringBetween("", "abc")); + assertEquals("", StringUtils.substringBetween(" ", " ")); + assertEquals(null, StringUtils.substringBetween("abc", null)); + assertEquals("", StringUtils.substringBetween("abc", "")); + assertEquals(null, StringUtils.substringBetween("abc", "a")); + assertEquals("bc", StringUtils.substringBetween("abca", "a")); + assertEquals("bc", StringUtils.substringBetween("abcabca", "a")); + assertEquals("bar", StringUtils.substringBetween("\nbar\n", "\n")); + } + + public void testSubstringBetween_StringStringString() { + assertEquals(null, StringUtils.substringBetween(null, "", "")); + assertEquals(null, StringUtils.substringBetween("", null, "")); + assertEquals(null, StringUtils.substringBetween("", "", null)); + assertEquals("", StringUtils.substringBetween("", "", "")); + assertEquals("", StringUtils.substringBetween("foo", "", "")); + assertEquals(null, StringUtils.substringBetween("foo", "", "]")); + assertEquals(null, StringUtils.substringBetween("foo", "[", "]")); + assertEquals("", StringUtils.substringBetween(" ", " ", " ")); + assertEquals("bar", StringUtils.substringBetween("bar", "", "") ); + } + + /** + * Tests the substringsBetween method that returns an String Array of substrings. + */ + public void testSubstringsBetween_StringStringString() { + + String[] results = StringUtils.substringsBetween("[one], [two], [three]", "[", "]"); + assertEquals(3, results.length); + assertEquals("one", results[0]); + assertEquals("two", results[1]); + assertEquals("three", results[2]); + + results = StringUtils.substringsBetween("[one], [two], three", "[", "]"); + assertEquals(2, results.length); + assertEquals("one", results[0]); + assertEquals("two", results[1]); + + results = StringUtils.substringsBetween("[one], [two], three]", "[", "]"); + assertEquals(2, results.length); + assertEquals("one", results[0]); + assertEquals("two", results[1]); + + results = StringUtils.substringsBetween("[one], two], three]", "[", "]"); + assertEquals(1, results.length); + assertEquals("one", results[0]); + + results = StringUtils.substringsBetween("one], two], [three]", "[", "]"); + assertEquals(1, results.length); + assertEquals("three", results[0]); + + // 'ab hello ba' will match, but 'ab non ba' won't + // this is because the 'a' is shared between the two and can't be matched twice + results = StringUtils.substringsBetween("aabhellobabnonba", "ab", "ba"); + assertEquals(1, results.length); + assertEquals("hello", results[0]); + + results = StringUtils.substringsBetween("one, two, three", "[", "]"); + assertNull(results); + + results = StringUtils.substringsBetween("[one, two, three", "[", "]"); + assertNull(results); + + results = StringUtils.substringsBetween("one, two, three]", "[", "]"); + assertNull(results); + + results = StringUtils.substringsBetween("[one], [two], [three]", "[", null); + assertNull(results); + + results = StringUtils.substringsBetween("[one], [two], [three]", null, "]"); + assertNull(results); + + results = StringUtils.substringsBetween("[one], [two], [three]", "", ""); + assertNull(results); + + results = StringUtils.substringsBetween(null, "[", "]"); + assertNull(results); + + results = StringUtils.substringsBetween("", "[", "]"); + assertEquals(0, results.length); + } + + //----------------------------------------------------------------------- + public void testCountMatches_String() { + assertEquals(0, StringUtils.countMatches(null, null)); + assertEquals(0, StringUtils.countMatches("blah", null)); + assertEquals(0, StringUtils.countMatches(null, "DD")); + + assertEquals(0, StringUtils.countMatches("x", "")); + assertEquals(0, StringUtils.countMatches("", "")); + + assertEquals(3, + StringUtils.countMatches("one long someone sentence of one", "one")); + assertEquals(0, + StringUtils.countMatches("one long someone sentence of one", "two")); + assertEquals(4, + StringUtils.countMatches("oooooooooooo", "ooo")); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/StringUtilsTest.java b/ApacheCommonsLang/org/apache/commons/lang3/StringUtilsTest.java new file mode 100644 index 0000000..1a94b0b --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/StringUtilsTest.java @@ -0,0 +1,2117 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.io.UnsupportedEncodingException; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.nio.CharBuffer; +import java.util.Arrays; +import java.util.Collections; +import java.util.Iterator; +import java.util.Locale; + +import org.apache.commons.lang3.text.WordUtils; +import org.junit.Test; + +/** + * Unit tests {@link org.apache.commons.lang3.StringUtils}. + * + * @version $Id: StringUtilsTest.java 1199724 2011-11-09 12:51:52Z sebb $ + */ +public class StringUtilsTest { + + static final String WHITESPACE; + static final String NON_WHITESPACE; + static final String TRIMMABLE; + static final String NON_TRIMMABLE; + static { + String ws = ""; + String nws = ""; + String tr = ""; + String ntr = ""; + for (int i = 0; i < Character.MAX_VALUE; i++) { + if (Character.isWhitespace((char) i)) { + ws += String.valueOf((char) i); + if (i > 32) { + ntr += String.valueOf((char) i); + } + } else if (i < 40) { + nws += String.valueOf((char) i); + } + } + for (int i = 0; i <= 32; i++) { + tr += String.valueOf((char) i); + } + WHITESPACE = ws; + NON_WHITESPACE = nws; + TRIMMABLE = tr; + NON_TRIMMABLE = ntr; + } + + private static final String[] ARRAY_LIST = { "foo", "bar", "baz" }; + private static final String[] EMPTY_ARRAY_LIST = {}; + private static final String[] NULL_ARRAY_LIST = {null}; + private static final Object[] NULL_TO_STRING_LIST = { + new Object(){ + @Override + public String toString() { + return null; + } + } + }; + private static final String[] MIXED_ARRAY_LIST = {null, "", "foo"}; + private static final Object[] MIXED_TYPE_LIST = {"foo", Long.valueOf(2L)}; + + private static final String SEPARATOR = ","; + private static final char SEPARATOR_CHAR = ';'; + + private static final String TEXT_LIST = "foo,bar,baz"; + private static final String TEXT_LIST_CHAR = "foo;bar;baz"; + private static final String TEXT_LIST_NOSEP = "foobarbaz"; + + private static final String FOO_UNCAP = "foo"; + private static final String FOO_CAP = "Foo"; + + private static final String SENTENCE_UNCAP = "foo bar baz"; + private static final String SENTENCE_CAP = "Foo Bar Baz"; + + //----------------------------------------------------------------------- + @Test + public void testConstructor() { + assertNotNull(new StringUtils()); + Constructor[] cons = StringUtils.class.getDeclaredConstructors(); + assertEquals(1, cons.length); + assertTrue(Modifier.isPublic(cons[0].getModifiers())); + assertTrue(Modifier.isPublic(StringUtils.class.getModifiers())); + assertFalse(Modifier.isFinal(StringUtils.class.getModifiers())); + } + + //----------------------------------------------------------------------- + @Test + public void testCaseFunctions() { + assertEquals(null, StringUtils.upperCase(null)); + assertEquals(null, StringUtils.upperCase(null, Locale.ENGLISH)); + assertEquals(null, StringUtils.lowerCase(null)); + assertEquals(null, StringUtils.lowerCase(null, Locale.ENGLISH)); + assertEquals(null, StringUtils.capitalize(null)); + assertEquals(null, StringUtils.uncapitalize(null)); + + assertEquals("capitalize(empty-string) failed", + "", StringUtils.capitalize("") ); + assertEquals("capitalize(single-char-string) failed", + "X", StringUtils.capitalize("x") ); + + assertEquals("uncapitalize(String) failed", + FOO_UNCAP, StringUtils.uncapitalize(FOO_CAP) ); + assertEquals("uncapitalize(empty-string) failed", + "", StringUtils.uncapitalize("") ); + assertEquals("uncapitalize(single-char-string) failed", + "x", StringUtils.uncapitalize("X") ); + + // reflection type of tests: Sentences. + assertEquals("uncapitalize(capitalize(String)) failed", + SENTENCE_UNCAP, StringUtils.uncapitalize(StringUtils.capitalize(SENTENCE_UNCAP)) ); + assertEquals("capitalize(uncapitalize(String)) failed", + SENTENCE_CAP, StringUtils.capitalize(StringUtils.uncapitalize(SENTENCE_CAP)) ); + + // reflection type of tests: One word. + assertEquals("uncapitalize(capitalize(String)) failed", + FOO_UNCAP, StringUtils.uncapitalize(StringUtils.capitalize(FOO_UNCAP)) ); + assertEquals("capitalize(uncapitalize(String)) failed", + FOO_CAP, StringUtils.capitalize(StringUtils.uncapitalize(FOO_CAP)) ); + + assertEquals("upperCase(String) failed", + "FOO TEST THING", StringUtils.upperCase("fOo test THING") ); + assertEquals("upperCase(empty-string) failed", + "", StringUtils.upperCase("") ); + assertEquals("lowerCase(String) failed", + "foo test thing", StringUtils.lowerCase("fOo test THING") ); + assertEquals("lowerCase(empty-string) failed", + "", StringUtils.lowerCase("") ); + + assertEquals("upperCase(String, Locale) failed", + "FOO TEST THING", StringUtils.upperCase("fOo test THING", Locale.ENGLISH) ); + assertEquals("upperCase(empty-string, Locale) failed", + "", StringUtils.upperCase("", Locale.ENGLISH) ); + assertEquals("lowerCase(String, Locale) failed", + "foo test thing", StringUtils.lowerCase("fOo test THING", Locale.ENGLISH) ); + assertEquals("lowerCase(empty-string, Locale) failed", + "", StringUtils.lowerCase("", Locale.ENGLISH) ); + } + + @Test + public void testSwapCase_String() { + assertEquals(null, StringUtils.swapCase(null)); + assertEquals("", StringUtils.swapCase("")); + assertEquals(" ", StringUtils.swapCase(" ")); + + assertEquals("i", WordUtils.swapCase("I") ); + assertEquals("I", WordUtils.swapCase("i") ); + assertEquals("I AM HERE 123", StringUtils.swapCase("i am here 123") ); + assertEquals("i aM hERE 123", StringUtils.swapCase("I Am Here 123") ); + assertEquals("I AM here 123", StringUtils.swapCase("i am HERE 123") ); + assertEquals("i am here 123", StringUtils.swapCase("I AM HERE 123") ); + + String test = "This String contains a TitleCase character: \u01C8"; + String expect = "tHIS sTRING CONTAINS A tITLEcASE CHARACTER: \u01C9"; + assertEquals(expect, WordUtils.swapCase(test)); + } + + //----------------------------------------------------------------------- + @Test + public void testJoin_Objects() { + assertEquals("abc", StringUtils.join("a", "b", "c")); + assertEquals("a", StringUtils.join(null, "", "a")); + assertEquals(null, StringUtils.join((Object[])null)); + } + + @Test + public void testJoin_Objectarray() { +// assertEquals(null, StringUtils.join(null)); // generates warning + assertEquals(null, StringUtils.join((Object[]) null)); // equivalent explicit cast + // test additional varargs calls + assertEquals("", StringUtils.join()); // empty array + assertEquals("", StringUtils.join((Object) null)); // => new Object[]{null} + + assertEquals("", StringUtils.join(EMPTY_ARRAY_LIST)); + assertEquals("", StringUtils.join(NULL_ARRAY_LIST)); + assertEquals("null", StringUtils.join(NULL_TO_STRING_LIST)); + assertEquals("abc", StringUtils.join(new String[] {"a", "b", "c"})); + assertEquals("a", StringUtils.join(new String[] {null, "a", ""})); + assertEquals("foo", StringUtils.join(MIXED_ARRAY_LIST)); + assertEquals("foo2", StringUtils.join(MIXED_TYPE_LIST)); + } + + @Test + public void testJoin_ArrayChar() { + assertEquals(null, StringUtils.join((Object[]) null, ',')); + assertEquals(TEXT_LIST_CHAR, StringUtils.join(ARRAY_LIST, SEPARATOR_CHAR)); + assertEquals("", StringUtils.join(EMPTY_ARRAY_LIST, SEPARATOR_CHAR)); + assertEquals(";;foo", StringUtils.join(MIXED_ARRAY_LIST, SEPARATOR_CHAR)); + assertEquals("foo;2", StringUtils.join(MIXED_TYPE_LIST, SEPARATOR_CHAR)); + + assertEquals("/", StringUtils.join(MIXED_ARRAY_LIST, '/', 0, MIXED_ARRAY_LIST.length-1)); + assertEquals("foo", StringUtils.join(MIXED_TYPE_LIST, '/', 0, 1)); + assertEquals("null", StringUtils.join(NULL_TO_STRING_LIST,'/', 0, 1)); + assertEquals("foo/2", StringUtils.join(MIXED_TYPE_LIST, '/', 0, 2)); + assertEquals("2", StringUtils.join(MIXED_TYPE_LIST, '/', 1, 2)); + assertEquals("", StringUtils.join(MIXED_TYPE_LIST, '/', 2, 1)); + } + + @Test + public void testJoin_ArrayString() { + assertEquals(null, StringUtils.join((Object[]) null, null)); + assertEquals(TEXT_LIST_NOSEP, StringUtils.join(ARRAY_LIST, null)); + assertEquals(TEXT_LIST_NOSEP, StringUtils.join(ARRAY_LIST, "")); + + assertEquals("", StringUtils.join(NULL_ARRAY_LIST, null)); + + assertEquals("", StringUtils.join(EMPTY_ARRAY_LIST, null)); + assertEquals("", StringUtils.join(EMPTY_ARRAY_LIST, "")); + assertEquals("", StringUtils.join(EMPTY_ARRAY_LIST, SEPARATOR)); + + assertEquals(TEXT_LIST, StringUtils.join(ARRAY_LIST, SEPARATOR)); + assertEquals(",,foo", StringUtils.join(MIXED_ARRAY_LIST, SEPARATOR)); + assertEquals("foo,2", StringUtils.join(MIXED_TYPE_LIST, SEPARATOR)); + + assertEquals("/", StringUtils.join(MIXED_ARRAY_LIST, "/", 0, MIXED_ARRAY_LIST.length-1)); + assertEquals("", StringUtils.join(MIXED_ARRAY_LIST, "", 0, MIXED_ARRAY_LIST.length-1)); + assertEquals("foo", StringUtils.join(MIXED_TYPE_LIST, "/", 0, 1)); + assertEquals("foo/2", StringUtils.join(MIXED_TYPE_LIST, "/", 0, 2)); + assertEquals("2", StringUtils.join(MIXED_TYPE_LIST, "/", 1, 2)); + assertEquals("", StringUtils.join(MIXED_TYPE_LIST, "/", 2, 1)); + } + + @Test + public void testJoin_IteratorChar() { + assertEquals(null, StringUtils.join((Iterator) null, ',')); + assertEquals(TEXT_LIST_CHAR, StringUtils.join(Arrays.asList(ARRAY_LIST).iterator(), SEPARATOR_CHAR)); + assertEquals("", StringUtils.join(Arrays.asList(NULL_ARRAY_LIST).iterator(), SEPARATOR_CHAR)); + assertEquals("", StringUtils.join(Arrays.asList(EMPTY_ARRAY_LIST).iterator(), SEPARATOR_CHAR)); + assertEquals("foo", StringUtils.join(Collections.singleton("foo").iterator(), 'x')); + } + + @Test + public void testJoin_IteratorString() { + assertEquals(null, StringUtils.join((Iterator) null, null)); + assertEquals(TEXT_LIST_NOSEP, StringUtils.join(Arrays.asList(ARRAY_LIST).iterator(), null)); + assertEquals(TEXT_LIST_NOSEP, StringUtils.join(Arrays.asList(ARRAY_LIST).iterator(), "")); + assertEquals("foo", StringUtils.join(Collections.singleton("foo").iterator(), "x")); + assertEquals("foo", StringUtils.join(Collections.singleton("foo").iterator(), null)); + + assertEquals("", StringUtils.join(Arrays.asList(NULL_ARRAY_LIST).iterator(), null)); + + assertEquals("", StringUtils.join(Arrays.asList(EMPTY_ARRAY_LIST).iterator(), null)); + assertEquals("", StringUtils.join(Arrays.asList(EMPTY_ARRAY_LIST).iterator(), "")); + assertEquals("", StringUtils.join(Arrays.asList(EMPTY_ARRAY_LIST).iterator(), SEPARATOR)); + + assertEquals(TEXT_LIST, StringUtils.join(Arrays.asList(ARRAY_LIST).iterator(), SEPARATOR)); + } + + @Test + public void testJoin_IterableChar() { + assertEquals(null, StringUtils.join((Iterable) null, ',')); + assertEquals(TEXT_LIST_CHAR, StringUtils.join(Arrays.asList(ARRAY_LIST), SEPARATOR_CHAR)); + assertEquals("", StringUtils.join(Arrays.asList(NULL_ARRAY_LIST), SEPARATOR_CHAR)); + assertEquals("", StringUtils.join(Arrays.asList(EMPTY_ARRAY_LIST), SEPARATOR_CHAR)); + assertEquals("foo", StringUtils.join(Collections.singleton("foo"), 'x')); + } + + @Test + public void testJoin_IterableString() { + assertEquals(null, StringUtils.join((Iterable) null, null)); + assertEquals(TEXT_LIST_NOSEP, StringUtils.join(Arrays.asList(ARRAY_LIST), null)); + assertEquals(TEXT_LIST_NOSEP, StringUtils.join(Arrays.asList(ARRAY_LIST), "")); + assertEquals("foo", StringUtils.join(Collections.singleton("foo"), "x")); + assertEquals("foo", StringUtils.join(Collections.singleton("foo"), null)); + + assertEquals("", StringUtils.join(Arrays.asList(NULL_ARRAY_LIST), null)); + + assertEquals("", StringUtils.join(Arrays.asList(EMPTY_ARRAY_LIST), null)); + assertEquals("", StringUtils.join(Arrays.asList(EMPTY_ARRAY_LIST), "")); + assertEquals("", StringUtils.join(Arrays.asList(EMPTY_ARRAY_LIST), SEPARATOR)); + + assertEquals(TEXT_LIST, StringUtils.join(Arrays.asList(ARRAY_LIST), SEPARATOR)); + } + + @Test + public void testSplit_String() { + assertArrayEquals(null, StringUtils.split(null)); + assertEquals(0, StringUtils.split("").length); + + String str = "a b .c"; + String[] res = StringUtils.split(str); + assertEquals(3, res.length); + assertEquals("a", res[0]); + assertEquals("b", res[1]); + assertEquals(".c", res[2]); + + str = " a "; + res = StringUtils.split(str); + assertEquals(1, res.length); + assertEquals("a", res[0]); + + str = "a" + WHITESPACE + "b" + NON_WHITESPACE + "c"; + res = StringUtils.split(str); + assertEquals(2, res.length); + assertEquals("a", res[0]); + assertEquals("b" + NON_WHITESPACE + "c", res[1]); + } + + @Test + public void testSplit_StringChar() { + assertArrayEquals(null, StringUtils.split(null, '.')); + assertEquals(0, StringUtils.split("", '.').length); + + String str = "a.b.. c"; + String[] res = StringUtils.split(str, '.'); + assertEquals(3, res.length); + assertEquals("a", res[0]); + assertEquals("b", res[1]); + assertEquals(" c", res[2]); + + str = ".a."; + res = StringUtils.split(str, '.'); + assertEquals(1, res.length); + assertEquals("a", res[0]); + + str = "a b c"; + res = StringUtils.split(str,' '); + assertEquals(3, res.length); + assertEquals("a", res[0]); + assertEquals("b", res[1]); + assertEquals("c", res[2]); + } + + @Test + public void testSplit_StringString_StringStringInt() { + assertArrayEquals(null, StringUtils.split(null, ".")); + assertArrayEquals(null, StringUtils.split(null, ".", 3)); + + assertEquals(0, StringUtils.split("", ".").length); + assertEquals(0, StringUtils.split("", ".", 3).length); + + innerTestSplit('.', ".", ' '); + innerTestSplit('.', ".", ','); + innerTestSplit('.', ".,", 'x'); + for (int i = 0; i < WHITESPACE.length(); i++) { + for (int j = 0; j < NON_WHITESPACE.length(); j++) { + innerTestSplit(WHITESPACE.charAt(i), null, NON_WHITESPACE.charAt(j)); + innerTestSplit(WHITESPACE.charAt(i), String.valueOf(WHITESPACE.charAt(i)), NON_WHITESPACE.charAt(j)); + } + } + + String[] results; + String[] expectedResults = {"ab", "de fg"}; + results = StringUtils.split("ab de fg", null, 2); + assertEquals(expectedResults.length, results.length); + for (int i = 0; i < expectedResults.length; i++) { + assertEquals(expectedResults[i], results[i]); + } + + String[] expectedResults2 = {"ab", "cd:ef"}; + results = StringUtils.split("ab:cd:ef",":", 2); + assertEquals(expectedResults2.length, results.length); + for (int i = 0; i < expectedResults2.length; i++) { + assertEquals(expectedResults2[i], results[i]); + } + } + + private void innerTestSplit(char separator, String sepStr, char noMatch) { + String msg = "Failed on separator hex(" + Integer.toHexString(separator) + + "), noMatch hex(" + Integer.toHexString(noMatch) + "), sepStr(" + sepStr + ")"; + + final String str = "a" + separator + "b" + separator + separator + noMatch + "c"; + String[] res; + // (str, sepStr) + res = StringUtils.split(str, sepStr); + assertEquals(msg, 3, res.length); + assertEquals(msg, "a", res[0]); + assertEquals(msg, "b", res[1]); + assertEquals(msg, noMatch + "c", res[2]); + + final String str2 = separator + "a" + separator; + res = StringUtils.split(str2, sepStr); + assertEquals(msg, 1, res.length); + assertEquals(msg, "a", res[0]); + + res = StringUtils.split(str, sepStr, -1); + assertEquals(msg, 3, res.length); + assertEquals(msg, "a", res[0]); + assertEquals(msg, "b", res[1]); + assertEquals(msg, noMatch + "c", res[2]); + + res = StringUtils.split(str, sepStr, 0); + assertEquals(msg, 3, res.length); + assertEquals(msg, "a", res[0]); + assertEquals(msg, "b", res[1]); + assertEquals(msg, noMatch + "c", res[2]); + + res = StringUtils.split(str, sepStr, 1); + assertEquals(msg, 1, res.length); + assertEquals(msg, str, res[0]); + + res = StringUtils.split(str, sepStr, 2); + assertEquals(msg, 2, res.length); + assertEquals(msg, "a", res[0]); + assertEquals(msg, str.substring(2), res[1]); + } + + @Test + public void testSplitByWholeString_StringStringBoolean() { + assertArrayEquals( null, StringUtils.splitByWholeSeparator( null, "." ) ) ; + + assertEquals( 0, StringUtils.splitByWholeSeparator( "", "." ).length ) ; + + String stringToSplitOnNulls = "ab de fg" ; + String[] splitOnNullExpectedResults = { "ab", "de", "fg" } ; + + String[] splitOnNullResults = StringUtils.splitByWholeSeparator( stringToSplitOnNulls, null ) ; + assertEquals( splitOnNullExpectedResults.length, splitOnNullResults.length ) ; + for ( int i = 0 ; i < splitOnNullExpectedResults.length ; i+= 1 ) { + assertEquals( splitOnNullExpectedResults[i], splitOnNullResults[i] ) ; + } + + String stringToSplitOnCharactersAndString = "abstemiouslyaeiouyabstemiously" ; + + String[] splitOnStringExpectedResults = { "abstemiously", "abstemiously" } ; + String[] splitOnStringResults = StringUtils.splitByWholeSeparator( stringToSplitOnCharactersAndString, "aeiouy" ) ; + assertEquals( splitOnStringExpectedResults.length, splitOnStringResults.length ) ; + for ( int i = 0 ; i < splitOnStringExpectedResults.length ; i+= 1 ) { + assertEquals( splitOnStringExpectedResults[i], splitOnStringResults[i] ) ; + } + + String[] splitWithMultipleSeparatorExpectedResults = {"ab", "cd", "ef"}; + String[] splitWithMultipleSeparator = StringUtils.splitByWholeSeparator("ab:cd::ef", ":"); + assertEquals( splitWithMultipleSeparatorExpectedResults.length, splitWithMultipleSeparator.length ); + for( int i = 0; i < splitWithMultipleSeparatorExpectedResults.length ; i++ ) { + assertEquals( splitWithMultipleSeparatorExpectedResults[i], splitWithMultipleSeparator[i] ) ; + } + } + + @Test + public void testSplitByWholeString_StringStringBooleanInt() { + assertArrayEquals( null, StringUtils.splitByWholeSeparator( null, ".", 3 ) ) ; + + assertEquals( 0, StringUtils.splitByWholeSeparator( "", ".", 3 ).length ) ; + + String stringToSplitOnNulls = "ab de fg" ; + String[] splitOnNullExpectedResults = { "ab", "de fg" } ; + //String[] splitOnNullExpectedResults = { "ab", "de" } ; + + String[] splitOnNullResults = StringUtils.splitByWholeSeparator( stringToSplitOnNulls, null, 2 ) ; + assertEquals( splitOnNullExpectedResults.length, splitOnNullResults.length ) ; + for ( int i = 0 ; i < splitOnNullExpectedResults.length ; i+= 1 ) { + assertEquals( splitOnNullExpectedResults[i], splitOnNullResults[i] ) ; + } + + String stringToSplitOnCharactersAndString = "abstemiouslyaeiouyabstemiouslyaeiouyabstemiously" ; + + String[] splitOnStringExpectedResults = { "abstemiously", "abstemiouslyaeiouyabstemiously" } ; + //String[] splitOnStringExpectedResults = { "abstemiously", "abstemiously" } ; + String[] splitOnStringResults = StringUtils.splitByWholeSeparator( stringToSplitOnCharactersAndString, "aeiouy", 2 ) ; + assertEquals( splitOnStringExpectedResults.length, splitOnStringResults.length ) ; + for ( int i = 0 ; i < splitOnStringExpectedResults.length ; i++ ) { + assertEquals( splitOnStringExpectedResults[i], splitOnStringResults[i] ) ; + } + } + + @Test + public void testSplitByWholeSeparatorPreserveAllTokens_StringStringInt() { + assertArrayEquals( null, StringUtils.splitByWholeSeparatorPreserveAllTokens( null, ".", -1 ) ) ; + + assertEquals( 0, StringUtils.splitByWholeSeparatorPreserveAllTokens( "", ".", -1 ).length ) ; + + // test whitespace + String input = "ab de fg" ; + String[] expected = new String[] { "ab", "", "", "de", "fg" } ; + + String[] actual = StringUtils.splitByWholeSeparatorPreserveAllTokens( input, null, -1 ) ; + assertEquals( expected.length, actual.length ) ; + for ( int i = 0 ; i < actual.length ; i+= 1 ) { + assertEquals( expected[i], actual[i] ); + } + + // test delimiter singlechar + input = "1::2:::3::::4"; + expected = new String[] { "1", "", "2", "", "", "3", "", "", "", "4" }; + + actual = StringUtils.splitByWholeSeparatorPreserveAllTokens( input, ":", -1 ) ; + assertEquals( expected.length, actual.length ) ; + for ( int i = 0 ; i < actual.length ; i+= 1 ) { + assertEquals( expected[i], actual[i] ); + } + + // test delimiter multichar + input = "1::2:::3::::4"; + expected = new String[] { "1", "2", ":3", "", "4" }; + + actual = StringUtils.splitByWholeSeparatorPreserveAllTokens( input, "::", -1 ) ; + assertEquals( expected.length, actual.length ) ; + for ( int i = 0 ; i < actual.length ; i+= 1 ) { + assertEquals( expected[i], actual[i] ); + } + + // test delimiter char with max + input = "1::2::3:4"; + expected = new String[] { "1", "", "2", ":3:4" }; + + actual = StringUtils.splitByWholeSeparatorPreserveAllTokens( input, ":", 4 ) ; + assertEquals( expected.length, actual.length ) ; + for ( int i = 0 ; i < actual.length ; i+= 1 ) { + assertEquals( expected[i], actual[i] ); + } + } + + @Test + public void testSplitPreserveAllTokens_String() { + assertArrayEquals(null, StringUtils.splitPreserveAllTokens(null)); + assertEquals(0, StringUtils.splitPreserveAllTokens("").length); + + String str = "abc def"; + String[] res = StringUtils.splitPreserveAllTokens(str); + assertEquals(2, res.length); + assertEquals("abc", res[0]); + assertEquals("def", res[1]); + + str = "abc def"; + res = StringUtils.splitPreserveAllTokens(str); + assertEquals(3, res.length); + assertEquals("abc", res[0]); + assertEquals("", res[1]); + assertEquals("def", res[2]); + + str = " abc "; + res = StringUtils.splitPreserveAllTokens(str); + assertEquals(3, res.length); + assertEquals("", res[0]); + assertEquals("abc", res[1]); + assertEquals("", res[2]); + + str = "a b .c"; + res = StringUtils.splitPreserveAllTokens(str); + assertEquals(3, res.length); + assertEquals("a", res[0]); + assertEquals("b", res[1]); + assertEquals(".c", res[2]); + + str = " a b .c"; + res = StringUtils.splitPreserveAllTokens(str); + assertEquals(4, res.length); + assertEquals("", res[0]); + assertEquals("a", res[1]); + assertEquals("b", res[2]); + assertEquals(".c", res[3]); + + str = "a b .c"; + res = StringUtils.splitPreserveAllTokens(str); + assertEquals(5, res.length); + assertEquals("a", res[0]); + assertEquals("", res[1]); + assertEquals("b", res[2]); + assertEquals("", res[3]); + assertEquals(".c", res[4]); + + str = " a "; + res = StringUtils.splitPreserveAllTokens(str); + assertEquals(4, res.length); + assertEquals("", res[0]); + assertEquals("a", res[1]); + assertEquals("", res[2]); + assertEquals("", res[3]); + + str = " a b"; + res = StringUtils.splitPreserveAllTokens(str); + assertEquals(4, res.length); + assertEquals("", res[0]); + assertEquals("a", res[1]); + assertEquals("", res[2]); + assertEquals("b", res[3]); + + str = "a" + WHITESPACE + "b" + NON_WHITESPACE + "c"; + res = StringUtils.splitPreserveAllTokens(str); + assertEquals(WHITESPACE.length() + 1, res.length); + assertEquals("a", res[0]); + for(int i = 1; i < WHITESPACE.length()-1; i++) + { + assertEquals("", res[i]); + } + assertEquals("b" + NON_WHITESPACE + "c", res[WHITESPACE.length()]); + } + + @Test + public void testSplitPreserveAllTokens_StringChar() { + assertArrayEquals(null, StringUtils.splitPreserveAllTokens(null, '.')); + assertEquals(0, StringUtils.splitPreserveAllTokens("", '.').length); + + String str = "a.b. c"; + String[] res = StringUtils.splitPreserveAllTokens(str, '.'); + assertEquals(3, res.length); + assertEquals("a", res[0]); + assertEquals("b", res[1]); + assertEquals(" c", res[2]); + + str = "a.b.. c"; + res = StringUtils.splitPreserveAllTokens(str, '.'); + assertEquals(4, res.length); + assertEquals("a", res[0]); + assertEquals("b", res[1]); + assertEquals("", res[2]); + assertEquals(" c", res[3]); + + str = ".a."; + res = StringUtils.splitPreserveAllTokens(str, '.'); + assertEquals(3, res.length); + assertEquals("", res[0]); + assertEquals("a", res[1]); + assertEquals("", res[2]); + + str = ".a.."; + res = StringUtils.splitPreserveAllTokens(str, '.'); + assertEquals(4, res.length); + assertEquals("", res[0]); + assertEquals("a", res[1]); + assertEquals("", res[2]); + assertEquals("", res[3]); + + str = "..a."; + res = StringUtils.splitPreserveAllTokens(str, '.'); + assertEquals(4, res.length); + assertEquals("", res[0]); + assertEquals("", res[1]); + assertEquals("a", res[2]); + assertEquals("", res[3]); + + str = "..a"; + res = StringUtils.splitPreserveAllTokens(str, '.'); + assertEquals(3, res.length); + assertEquals("", res[0]); + assertEquals("", res[1]); + assertEquals("a", res[2]); + + str = "a b c"; + res = StringUtils.splitPreserveAllTokens(str,' '); + assertEquals(3, res.length); + assertEquals("a", res[0]); + assertEquals("b", res[1]); + assertEquals("c", res[2]); + + str = "a b c"; + res = StringUtils.splitPreserveAllTokens(str,' '); + assertEquals(5, res.length); + assertEquals("a", res[0]); + assertEquals("", res[1]); + assertEquals("b", res[2]); + assertEquals("", res[3]); + assertEquals("c", res[4]); + + str = " a b c"; + res = StringUtils.splitPreserveAllTokens(str,' '); + assertEquals(4, res.length); + assertEquals("", res[0]); + assertEquals("a", res[1]); + assertEquals("b", res[2]); + assertEquals("c", res[3]); + + str = " a b c"; + res = StringUtils.splitPreserveAllTokens(str,' '); + assertEquals(5, res.length); + assertEquals("", res[0]); + assertEquals("", res[1]); + assertEquals("a", res[2]); + assertEquals("b", res[3]); + assertEquals("c", res[4]); + + str = "a b c "; + res = StringUtils.splitPreserveAllTokens(str,' '); + assertEquals(4, res.length); + assertEquals("a", res[0]); + assertEquals("b", res[1]); + assertEquals("c", res[2]); + assertEquals("", res[3]); + + str = "a b c "; + res = StringUtils.splitPreserveAllTokens(str,' '); + assertEquals(5, res.length); + assertEquals("a", res[0]); + assertEquals("b", res[1]); + assertEquals("c", res[2]); + assertEquals("", res[3]); + assertEquals("", res[3]); + + // Match example in javadoc + { + String[] results; + String[] expectedResults = {"a", "", "b", "c"}; + results = StringUtils.splitPreserveAllTokens("a..b.c",'.'); + assertEquals(expectedResults.length, results.length); + for (int i = 0; i < expectedResults.length; i++) { + assertEquals(expectedResults[i], results[i]); + } + } + } + + @Test + public void testSplitPreserveAllTokens_StringString_StringStringInt() { + assertArrayEquals(null, StringUtils.splitPreserveAllTokens(null, ".")); + assertArrayEquals(null, StringUtils.splitPreserveAllTokens(null, ".", 3)); + + assertEquals(0, StringUtils.splitPreserveAllTokens("", ".").length); + assertEquals(0, StringUtils.splitPreserveAllTokens("", ".", 3).length); + + innerTestSplitPreserveAllTokens('.', ".", ' '); + innerTestSplitPreserveAllTokens('.', ".", ','); + innerTestSplitPreserveAllTokens('.', ".,", 'x'); + for (int i = 0; i < WHITESPACE.length(); i++) { + for (int j = 0; j < NON_WHITESPACE.length(); j++) { + innerTestSplitPreserveAllTokens(WHITESPACE.charAt(i), null, NON_WHITESPACE.charAt(j)); + innerTestSplitPreserveAllTokens(WHITESPACE.charAt(i), String.valueOf(WHITESPACE.charAt(i)), NON_WHITESPACE.charAt(j)); + } + } + + { + String[] results; + String[] expectedResults = {"ab", "de fg"}; + results = StringUtils.splitPreserveAllTokens("ab de fg", null, 2); + assertEquals(expectedResults.length, results.length); + for (int i = 0; i < expectedResults.length; i++) { + assertEquals(expectedResults[i], results[i]); + } + } + + { + String[] results; + String[] expectedResults = {"ab", " de fg"}; + results = StringUtils.splitPreserveAllTokens("ab de fg", null, 2); + assertEquals(expectedResults.length, results.length); + for (int i = 0; i < expectedResults.length; i++) { + assertEquals(expectedResults[i], results[i]); + } + } + + { + String[] results; + String[] expectedResults = {"ab", "::de:fg"}; + results = StringUtils.splitPreserveAllTokens("ab:::de:fg", ":", 2); + assertEquals(expectedResults.length, results.length); + for (int i = 0; i < expectedResults.length; i++) { + assertEquals(expectedResults[i], results[i]); + } + } + + { + String[] results; + String[] expectedResults = {"ab", "", " de fg"}; + results = StringUtils.splitPreserveAllTokens("ab de fg", null, 3); + assertEquals(expectedResults.length, results.length); + for (int i = 0; i < expectedResults.length; i++) { + assertEquals(expectedResults[i], results[i]); + } + } + + { + String[] results; + String[] expectedResults = {"ab", "", "", "de fg"}; + results = StringUtils.splitPreserveAllTokens("ab de fg", null, 4); + assertEquals(expectedResults.length, results.length); + for (int i = 0; i < expectedResults.length; i++) { + assertEquals(expectedResults[i], results[i]); + } + } + + { + String[] expectedResults = {"ab", "cd:ef"}; + String[] results; + results = StringUtils.splitPreserveAllTokens("ab:cd:ef",":", 2); + assertEquals(expectedResults.length, results.length); + for (int i = 0; i < expectedResults.length; i++) { + assertEquals(expectedResults[i], results[i]); + } + } + + { + String[] results; + String[] expectedResults = {"ab", ":cd:ef"}; + results = StringUtils.splitPreserveAllTokens("ab::cd:ef",":", 2); + assertEquals(expectedResults.length, results.length); + for (int i = 0; i < expectedResults.length; i++) { + assertEquals(expectedResults[i], results[i]); + } + } + + { + String[] results; + String[] expectedResults = {"ab", "", ":cd:ef"}; + results = StringUtils.splitPreserveAllTokens("ab:::cd:ef",":", 3); + assertEquals(expectedResults.length, results.length); + for (int i = 0; i < expectedResults.length; i++) { + assertEquals(expectedResults[i], results[i]); + } + } + + { + String[] results; + String[] expectedResults = {"ab", "", "", "cd:ef"}; + results = StringUtils.splitPreserveAllTokens("ab:::cd:ef",":", 4); + assertEquals(expectedResults.length, results.length); + for (int i = 0; i < expectedResults.length; i++) { + assertEquals(expectedResults[i], results[i]); + } + } + + { + String[] results; + String[] expectedResults = {"", "ab", "", "", "cd:ef"}; + results = StringUtils.splitPreserveAllTokens(":ab:::cd:ef",":", 5); + assertEquals(expectedResults.length, results.length); + for (int i = 0; i < expectedResults.length; i++) { + assertEquals(expectedResults[i], results[i]); + } + } + + { + String[] results; + String[] expectedResults = {"", "", "ab", "", "", "cd:ef"}; + results = StringUtils.splitPreserveAllTokens("::ab:::cd:ef",":", 6); + assertEquals(expectedResults.length, results.length); + for (int i = 0; i < expectedResults.length; i++) { + assertEquals(expectedResults[i], results[i]); + } + } + + } + + private void innerTestSplitPreserveAllTokens(char separator, String sepStr, char noMatch) { + String msg = "Failed on separator hex(" + Integer.toHexString(separator) + + "), noMatch hex(" + Integer.toHexString(noMatch) + "), sepStr(" + sepStr + ")"; + + final String str = "a" + separator + "b" + separator + separator + noMatch + "c"; + String[] res; + // (str, sepStr) + res = StringUtils.splitPreserveAllTokens(str, sepStr); + assertEquals(msg, 4, res.length); + assertEquals(msg, "a", res[0]); + assertEquals(msg, "b", res[1]); + assertEquals(msg, "", res[2]); + assertEquals(msg, noMatch + "c", res[3]); + + final String str2 = separator + "a" + separator; + res = StringUtils.splitPreserveAllTokens(str2, sepStr); + assertEquals(msg, 3, res.length); + assertEquals(msg, "", res[0]); + assertEquals(msg, "a", res[1]); + assertEquals(msg, "", res[2]); + + res = StringUtils.splitPreserveAllTokens(str, sepStr, -1); + assertEquals(msg, 4, res.length); + assertEquals(msg, "a", res[0]); + assertEquals(msg, "b", res[1]); + assertEquals(msg, "", res[2]); + assertEquals(msg, noMatch + "c", res[3]); + + res = StringUtils.splitPreserveAllTokens(str, sepStr, 0); + assertEquals(msg, 4, res.length); + assertEquals(msg, "a", res[0]); + assertEquals(msg, "b", res[1]); + assertEquals(msg, "", res[2]); + assertEquals(msg, noMatch + "c", res[3]); + + res = StringUtils.splitPreserveAllTokens(str, sepStr, 1); + assertEquals(msg, 1, res.length); + assertEquals(msg, str, res[0]); + + res = StringUtils.splitPreserveAllTokens(str, sepStr, 2); + assertEquals(msg, 2, res.length); + assertEquals(msg, "a", res[0]); + assertEquals(msg, str.substring(2), res[1]); + } + + @Test + public void testSplitByCharacterType() { + assertNull(StringUtils.splitByCharacterType(null)); + assertEquals(0, StringUtils.splitByCharacterType("").length); + + assertTrue(ArrayUtils.isEquals(new String[] { "ab", " ", "de", " ", + "fg" }, StringUtils.splitByCharacterType("ab de fg"))); + + assertTrue(ArrayUtils.isEquals(new String[] { "ab", " ", "de", " ", + "fg" }, StringUtils.splitByCharacterType("ab de fg"))); + + assertTrue(ArrayUtils.isEquals(new String[] { "ab", ":", "cd", ":", + "ef" }, StringUtils.splitByCharacterType("ab:cd:ef"))); + + assertTrue(ArrayUtils.isEquals(new String[] { "number", "5" }, + StringUtils.splitByCharacterType("number5"))); + + assertTrue(ArrayUtils.isEquals(new String[] { "foo", "B", "ar" }, + StringUtils.splitByCharacterType("fooBar"))); + + assertTrue(ArrayUtils.isEquals(new String[] { "foo", "200", "B", "ar" }, + StringUtils.splitByCharacterType("foo200Bar"))); + + assertTrue(ArrayUtils.isEquals(new String[] { "ASFR", "ules" }, + StringUtils.splitByCharacterType("ASFRules"))); + } + + @Test + public void testSplitByCharacterTypeCamelCase() { + assertNull(StringUtils.splitByCharacterTypeCamelCase(null)); + assertEquals(0, StringUtils.splitByCharacterTypeCamelCase("").length); + + assertTrue(ArrayUtils.isEquals(new String[] { "ab", " ", "de", " ", + "fg" }, StringUtils.splitByCharacterTypeCamelCase("ab de fg"))); + + assertTrue(ArrayUtils.isEquals(new String[] { "ab", " ", "de", " ", + "fg" }, StringUtils.splitByCharacterTypeCamelCase("ab de fg"))); + + assertTrue(ArrayUtils.isEquals(new String[] { "ab", ":", "cd", ":", + "ef" }, StringUtils.splitByCharacterTypeCamelCase("ab:cd:ef"))); + + assertTrue(ArrayUtils.isEquals(new String[] { "number", "5" }, + StringUtils.splitByCharacterTypeCamelCase("number5"))); + + assertTrue(ArrayUtils.isEquals(new String[] { "foo", "Bar" }, + StringUtils.splitByCharacterTypeCamelCase("fooBar"))); + + assertTrue(ArrayUtils.isEquals(new String[] { "foo", "200", "Bar" }, + StringUtils.splitByCharacterTypeCamelCase("foo200Bar"))); + + assertTrue(ArrayUtils.isEquals(new String[] { "ASF", "Rules" }, + StringUtils.splitByCharacterTypeCamelCase("ASFRules"))); + } + + @Test + public void testDeleteWhitespace_String() { + assertEquals(null, StringUtils.deleteWhitespace(null)); + assertEquals("", StringUtils.deleteWhitespace("")); + assertEquals("", StringUtils.deleteWhitespace(" \u000C \t\t\u001F\n\n \u000B ")); + assertEquals("", StringUtils.deleteWhitespace(StringUtilsTest.WHITESPACE)); + assertEquals(StringUtilsTest.NON_WHITESPACE, StringUtils.deleteWhitespace(StringUtilsTest.NON_WHITESPACE)); + // Note: u-2007 and u-000A both cause problems in the source code + // it should ignore 2007 but delete 000A + assertEquals("\u00A0\u202F", StringUtils.deleteWhitespace(" \u00A0 \t\t\n\n \u202F ")); + assertEquals("\u00A0\u202F", StringUtils.deleteWhitespace("\u00A0\u202F")); + assertEquals("test", StringUtils.deleteWhitespace("\u000Bt \t\n\u0009e\rs\n\n \tt")); + } + + @Test + public void testLang623() { + assertEquals("t", StringUtils.replaceChars("\u00DE", '\u00DE', 't')); + assertEquals("t", StringUtils.replaceChars("\u00FE", '\u00FE', 't')); + } + + @Test + public void testReplace_StringStringString() { + assertEquals(null, StringUtils.replace(null, null, null)); + assertEquals(null, StringUtils.replace(null, null, "any")); + assertEquals(null, StringUtils.replace(null, "any", null)); + assertEquals(null, StringUtils.replace(null, "any", "any")); + + assertEquals("", StringUtils.replace("", null, null)); + assertEquals("", StringUtils.replace("", null, "any")); + assertEquals("", StringUtils.replace("", "any", null)); + assertEquals("", StringUtils.replace("", "any", "any")); + + assertEquals("FOO", StringUtils.replace("FOO", "", "any")); + assertEquals("FOO", StringUtils.replace("FOO", null, "any")); + assertEquals("FOO", StringUtils.replace("FOO", "F", null)); + assertEquals("FOO", StringUtils.replace("FOO", null, null)); + + assertEquals("", StringUtils.replace("foofoofoo", "foo", "")); + assertEquals("barbarbar", StringUtils.replace("foofoofoo", "foo", "bar")); + assertEquals("farfarfar", StringUtils.replace("foofoofoo", "oo", "ar")); + } + + @Test + public void testReplace_StringStringStringInt() { + assertEquals(null, StringUtils.replace(null, null, null, 2)); + assertEquals(null, StringUtils.replace(null, null, "any", 2)); + assertEquals(null, StringUtils.replace(null, "any", null, 2)); + assertEquals(null, StringUtils.replace(null, "any", "any", 2)); + + assertEquals("", StringUtils.replace("", null, null, 2)); + assertEquals("", StringUtils.replace("", null, "any", 2)); + assertEquals("", StringUtils.replace("", "any", null, 2)); + assertEquals("", StringUtils.replace("", "any", "any", 2)); + + String str = new String(new char[] {'o', 'o', 'f', 'o', 'o'}); + assertSame(str, StringUtils.replace(str, "x", "", -1)); + + assertEquals("f", StringUtils.replace("oofoo", "o", "", -1)); + assertEquals("oofoo", StringUtils.replace("oofoo", "o", "", 0)); + assertEquals("ofoo", StringUtils.replace("oofoo", "o", "", 1)); + assertEquals("foo", StringUtils.replace("oofoo", "o", "", 2)); + assertEquals("fo", StringUtils.replace("oofoo", "o", "", 3)); + assertEquals("f", StringUtils.replace("oofoo", "o", "", 4)); + + assertEquals("f", StringUtils.replace("oofoo", "o", "", -5)); + assertEquals("f", StringUtils.replace("oofoo", "o", "", 1000)); + } + + @Test + public void testReplaceOnce_StringStringString() { + assertEquals(null, StringUtils.replaceOnce(null, null, null)); + assertEquals(null, StringUtils.replaceOnce(null, null, "any")); + assertEquals(null, StringUtils.replaceOnce(null, "any", null)); + assertEquals(null, StringUtils.replaceOnce(null, "any", "any")); + + assertEquals("", StringUtils.replaceOnce("", null, null)); + assertEquals("", StringUtils.replaceOnce("", null, "any")); + assertEquals("", StringUtils.replaceOnce("", "any", null)); + assertEquals("", StringUtils.replaceOnce("", "any", "any")); + + assertEquals("FOO", StringUtils.replaceOnce("FOO", "", "any")); + assertEquals("FOO", StringUtils.replaceOnce("FOO", null, "any")); + assertEquals("FOO", StringUtils.replaceOnce("FOO", "F", null)); + assertEquals("FOO", StringUtils.replaceOnce("FOO", null, null)); + + assertEquals("foofoo", StringUtils.replaceOnce("foofoofoo", "foo", "")); + } + + /** + * Test method for 'StringUtils.replaceEach(String, String[], String[])' + */ + @Test + public void testReplace_StringStringArrayStringArray() { + //JAVADOC TESTS START + assertNull(StringUtils.replaceEach(null, new String[]{"a"}, new String[]{"b"})); + assertEquals(StringUtils.replaceEach("", new String[]{"a"}, new String[]{"b"}),""); + assertEquals(StringUtils.replaceEach("aba", null, null),"aba"); + assertEquals(StringUtils.replaceEach("aba", new String[0], null),"aba"); + assertEquals(StringUtils.replaceEach("aba", null, new String[0]),"aba"); + assertEquals(StringUtils.replaceEach("aba", new String[]{"a"}, null),"aba"); + + assertEquals(StringUtils.replaceEach("aba", new String[]{"a"}, new String[]{""}),"b"); + assertEquals(StringUtils.replaceEach("aba", new String[]{null}, new String[]{"a"}),"aba"); + assertEquals(StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"w", "t"}),"wcte"); + assertEquals(StringUtils.replaceEach("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"}),"dcte"); + //JAVADOC TESTS END + + assertEquals("bcc", StringUtils.replaceEach("abc", new String[]{"a", "b"}, new String[]{"b", "c"})); + assertEquals("q651.506bera", StringUtils.replaceEach("d216.102oren", + new String[]{"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", + "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "A", "B", "C", "D", + "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", + "U", "V", "W", "X", "Y", "Z", "1", "2", "3", "4", "5", "6", "7", "8", "9"}, + new String[]{"n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "a", + "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "N", "O", "P", "Q", + "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "A", "B", "C", "D", "E", "F", "G", + "H", "I", "J", "K", "L", "M", "5", "6", "7", "8", "9", "1", "2", "3", "4"})); + + // Test null safety inside arrays - LANG-552 + assertEquals(StringUtils.replaceEach("aba", new String[]{"a"}, new String[]{null}),"aba"); + assertEquals(StringUtils.replaceEach("aba", new String[]{"a", "b"}, new String[]{"c", null}),"cbc"); + } + + /** + * Test method for 'StringUtils.replaceEachRepeatedly(String, String[], String[])' + */ + @Test + public void testReplace_StringStringArrayStringArrayBoolean() { + //JAVADOC TESTS START + assertNull(StringUtils.replaceEachRepeatedly(null, new String[]{"a"}, new String[]{"b"})); + assertEquals(StringUtils.replaceEachRepeatedly("", new String[]{"a"}, new String[]{"b"}),""); + assertEquals(StringUtils.replaceEachRepeatedly("aba", null, null),"aba"); + assertEquals(StringUtils.replaceEachRepeatedly("aba", new String[0], null),"aba"); + assertEquals(StringUtils.replaceEachRepeatedly("aba", null, new String[0]),"aba"); + assertEquals(StringUtils.replaceEachRepeatedly("aba", new String[0], null),"aba"); + + assertEquals(StringUtils.replaceEachRepeatedly("aba", new String[]{"a"}, new String[]{""}),"b"); + assertEquals(StringUtils.replaceEachRepeatedly("aba", new String[]{null}, new String[]{"a"}),"aba"); + assertEquals(StringUtils.replaceEachRepeatedly("abcde", new String[]{"ab", "d"}, new String[]{"w", "t"}),"wcte"); + assertEquals(StringUtils.replaceEachRepeatedly("abcde", new String[]{"ab", "d"}, new String[]{"d", "t"}),"tcte"); + + try { + StringUtils.replaceEachRepeatedly("abcde", new String[]{"ab", "d"}, new String[]{"d", "ab"}); + fail("Should be a circular reference"); + } catch (IllegalStateException e) {} + + //JAVADOC TESTS END + } + + @Test + public void testReplaceChars_StringCharChar() { + assertEquals(null, StringUtils.replaceChars(null, 'b', 'z')); + assertEquals("", StringUtils.replaceChars("", 'b', 'z')); + assertEquals("azcza", StringUtils.replaceChars("abcba", 'b', 'z')); + assertEquals("abcba", StringUtils.replaceChars("abcba", 'x', 'z')); + } + + @Test + public void testReplaceChars_StringStringString() { + assertEquals(null, StringUtils.replaceChars(null, null, null)); + assertEquals(null, StringUtils.replaceChars(null, "", null)); + assertEquals(null, StringUtils.replaceChars(null, "a", null)); + assertEquals(null, StringUtils.replaceChars(null, null, "")); + assertEquals(null, StringUtils.replaceChars(null, null, "x")); + + assertEquals("", StringUtils.replaceChars("", null, null)); + assertEquals("", StringUtils.replaceChars("", "", null)); + assertEquals("", StringUtils.replaceChars("", "a", null)); + assertEquals("", StringUtils.replaceChars("", null, "")); + assertEquals("", StringUtils.replaceChars("", null, "x")); + + assertEquals("abc", StringUtils.replaceChars("abc", null, null)); + assertEquals("abc", StringUtils.replaceChars("abc", null, "")); + assertEquals("abc", StringUtils.replaceChars("abc", null, "x")); + + assertEquals("abc", StringUtils.replaceChars("abc", "", null)); + assertEquals("abc", StringUtils.replaceChars("abc", "", "")); + assertEquals("abc", StringUtils.replaceChars("abc", "", "x")); + + assertEquals("ac", StringUtils.replaceChars("abc", "b", null)); + assertEquals("ac", StringUtils.replaceChars("abc", "b", "")); + assertEquals("axc", StringUtils.replaceChars("abc", "b", "x")); + + assertEquals("ayzya", StringUtils.replaceChars("abcba", "bc", "yz")); + assertEquals("ayya", StringUtils.replaceChars("abcba", "bc", "y")); + assertEquals("ayzya", StringUtils.replaceChars("abcba", "bc", "yzx")); + + assertEquals("abcba", StringUtils.replaceChars("abcba", "z", "w")); + assertSame("abcba", StringUtils.replaceChars("abcba", "z", "w")); + + // Javadoc examples: + assertEquals("jelly", StringUtils.replaceChars("hello", "ho", "jy")); + assertEquals("ayzya", StringUtils.replaceChars("abcba", "bc", "yz")); + assertEquals("ayya", StringUtils.replaceChars("abcba", "bc", "y")); + assertEquals("ayzya", StringUtils.replaceChars("abcba", "bc", "yzx")); + + // From http://issues.apache.org/bugzilla/show_bug.cgi?id=25454 + assertEquals("bcc", StringUtils.replaceChars("abc", "ab", "bc")); + assertEquals("q651.506bera", StringUtils.replaceChars("d216.102oren", + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789", + "nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM567891234")); + } + + @Test + public void testOverlay_StringStringIntInt() { + assertEquals(null, StringUtils.overlay(null, null, 2, 4)); + assertEquals(null, StringUtils.overlay(null, null, -2, -4)); + + assertEquals("", StringUtils.overlay("", null, 0, 0)); + assertEquals("", StringUtils.overlay("", "", 0, 0)); + assertEquals("zzzz", StringUtils.overlay("", "zzzz", 0, 0)); + assertEquals("zzzz", StringUtils.overlay("", "zzzz", 2, 4)); + assertEquals("zzzz", StringUtils.overlay("", "zzzz", -2, -4)); + + assertEquals("abef", StringUtils.overlay("abcdef", null, 2, 4)); + assertEquals("abef", StringUtils.overlay("abcdef", null, 4, 2)); + assertEquals("abef", StringUtils.overlay("abcdef", "", 2, 4)); + assertEquals("abef", StringUtils.overlay("abcdef", "", 4, 2)); + assertEquals("abzzzzef", StringUtils.overlay("abcdef", "zzzz", 2, 4)); + assertEquals("abzzzzef", StringUtils.overlay("abcdef", "zzzz", 4, 2)); + + assertEquals("zzzzef", StringUtils.overlay("abcdef", "zzzz", -1, 4)); + assertEquals("zzzzef", StringUtils.overlay("abcdef", "zzzz", 4, -1)); + assertEquals("zzzzabcdef", StringUtils.overlay("abcdef", "zzzz", -2, -1)); + assertEquals("zzzzabcdef", StringUtils.overlay("abcdef", "zzzz", -1, -2)); + assertEquals("abcdzzzz", StringUtils.overlay("abcdef", "zzzz", 4, 10)); + assertEquals("abcdzzzz", StringUtils.overlay("abcdef", "zzzz", 10, 4)); + assertEquals("abcdefzzzz", StringUtils.overlay("abcdef", "zzzz", 8, 10)); + assertEquals("abcdefzzzz", StringUtils.overlay("abcdef", "zzzz", 10, 8)); + } + + @Test + public void testRepeat_StringInt() { + assertEquals(null, StringUtils.repeat(null, 2)); + assertEquals("", StringUtils.repeat("ab", 0)); + assertEquals("", StringUtils.repeat("", 3)); + assertEquals("aaa", StringUtils.repeat("a", 3)); + assertEquals("ababab", StringUtils.repeat("ab", 3)); + assertEquals("abcabcabc", StringUtils.repeat("abc", 3)); + String str = StringUtils.repeat("a", 10000); // bigger than pad limit + assertEquals(10000, str.length()); + assertTrue(StringUtils.containsOnly(str, new char[] {'a'})); + } + + @Test + public void testRepeat_StringStringInt() { + assertEquals(null, StringUtils.repeat(null, null, 2)); + assertEquals(null, StringUtils.repeat(null, "x", 2)); + assertEquals("", StringUtils.repeat("", null, 2)); + + assertEquals("", StringUtils.repeat("ab", "", 0)); + assertEquals("", StringUtils.repeat("", "", 2)); + + assertEquals("xx", StringUtils.repeat("", "x", 3)); + + assertEquals("?, ?, ?", StringUtils.repeat("?", ", ", 3)); + } + + @Test + public void testChop() { + + String[][] chopCases = { + { FOO_UNCAP + "\r\n", FOO_UNCAP } , + { FOO_UNCAP + "\n" , FOO_UNCAP } , + { FOO_UNCAP + "\r", FOO_UNCAP }, + { FOO_UNCAP + " \r", FOO_UNCAP + " " }, + { "foo", "fo"}, + { "foo\nfoo", "foo\nfo" }, + { "\n", "" }, + { "\r", "" }, + { "\r\n", "" }, + { null, null }, + { "", "" }, + { "a", "" }, + }; + for (String[] chopCase : chopCases) { + String original = chopCase[0]; + String expectedResult = chopCase[1]; + assertEquals("chop(String) failed", + expectedResult, StringUtils.chop(original)); + } + } + + @Test + public void testChomp() { + + String[][] chompCases = { + { FOO_UNCAP + "\r\n", FOO_UNCAP }, + { FOO_UNCAP + "\n" , FOO_UNCAP }, + { FOO_UNCAP + "\r", FOO_UNCAP }, + { FOO_UNCAP + " \r", FOO_UNCAP + " " }, + { FOO_UNCAP, FOO_UNCAP }, + { FOO_UNCAP + "\n\n", FOO_UNCAP + "\n"}, + { FOO_UNCAP + "\r\n\r\n", FOO_UNCAP + "\r\n" }, + { "foo\nfoo", "foo\nfoo" }, + { "foo\n\rfoo", "foo\n\rfoo" }, + { "\n", "" }, + { "\r", "" }, + { "a", "a" }, + { "\r\n", "" }, + { "", "" }, + { null, null }, + { FOO_UNCAP + "\n\r", FOO_UNCAP + "\n"} + }; + for (String[] chompCase : chompCases) { + String original = chompCase[0]; + String expectedResult = chompCase[1]; + assertEquals("chomp(String) failed", + expectedResult, StringUtils.chomp(original)); + } + + assertEquals("chomp(String, String) failed", + "foo", StringUtils.chomp("foobar", "bar")); + assertEquals("chomp(String, String) failed", + "foobar", StringUtils.chomp("foobar", "baz")); + assertEquals("chomp(String, String) failed", + "foo", StringUtils.chomp("foo", "foooo")); + assertEquals("chomp(String, String) failed", + "foobar", StringUtils.chomp("foobar", "")); + assertEquals("chomp(String, String) failed", + "foobar", StringUtils.chomp("foobar", null)); + assertEquals("chomp(String, String) failed", + "", StringUtils.chomp("", "foo")); + assertEquals("chomp(String, String) failed", + "", StringUtils.chomp("", null)); + assertEquals("chomp(String, String) failed", + "", StringUtils.chomp("", "")); + assertEquals("chomp(String, String) failed", + null, StringUtils.chomp(null, "foo")); + assertEquals("chomp(String, String) failed", + null, StringUtils.chomp(null, null)); + assertEquals("chomp(String, String) failed", + null, StringUtils.chomp(null, "")); + assertEquals("chomp(String, String) failed", + "", StringUtils.chomp("foo", "foo")); + assertEquals("chomp(String, String) failed", + " ", StringUtils.chomp(" foo", "foo")); + assertEquals("chomp(String, String) failed", + "foo ", StringUtils.chomp("foo ", "foo")); + } + + //----------------------------------------------------------------------- + @Test + public void testRightPad_StringInt() { + assertEquals(null, StringUtils.rightPad(null, 5)); + assertEquals(" ", StringUtils.rightPad("", 5)); + assertEquals("abc ", StringUtils.rightPad("abc", 5)); + assertEquals("abc", StringUtils.rightPad("abc", 2)); + assertEquals("abc", StringUtils.rightPad("abc", -1)); + } + + @Test + public void testRightPad_StringIntChar() { + assertEquals(null, StringUtils.rightPad(null, 5, ' ')); + assertEquals(" ", StringUtils.rightPad("", 5, ' ')); + assertEquals("abc ", StringUtils.rightPad("abc", 5, ' ')); + assertEquals("abc", StringUtils.rightPad("abc", 2, ' ')); + assertEquals("abc", StringUtils.rightPad("abc", -1, ' ')); + assertEquals("abcxx", StringUtils.rightPad("abc", 5, 'x')); + String str = StringUtils.rightPad("aaa", 10000, 'a'); // bigger than pad length + assertEquals(10000, str.length()); + assertTrue(StringUtils.containsOnly(str, new char[] {'a'})); + } + + @Test + public void testRightPad_StringIntString() { + assertEquals(null, StringUtils.rightPad(null, 5, "-+")); + assertEquals(" ", StringUtils.rightPad("", 5, " ")); + assertEquals(null, StringUtils.rightPad(null, 8, null)); + assertEquals("abc-+-+", StringUtils.rightPad("abc", 7, "-+")); + assertEquals("abc-+~", StringUtils.rightPad("abc", 6, "-+~")); + assertEquals("abc-+", StringUtils.rightPad("abc", 5, "-+~")); + assertEquals("abc", StringUtils.rightPad("abc", 2, " ")); + assertEquals("abc", StringUtils.rightPad("abc", -1, " ")); + assertEquals("abc ", StringUtils.rightPad("abc", 5, null)); + assertEquals("abc ", StringUtils.rightPad("abc", 5, "")); + } + + //----------------------------------------------------------------------- + @Test + public void testLeftPad_StringInt() { + assertEquals(null, StringUtils.leftPad(null, 5)); + assertEquals(" ", StringUtils.leftPad("", 5)); + assertEquals(" abc", StringUtils.leftPad("abc", 5)); + assertEquals("abc", StringUtils.leftPad("abc", 2)); + } + + @Test + public void testLeftPad_StringIntChar() { + assertEquals(null, StringUtils.leftPad(null, 5, ' ')); + assertEquals(" ", StringUtils.leftPad("", 5, ' ')); + assertEquals(" abc", StringUtils.leftPad("abc", 5, ' ')); + assertEquals("xxabc", StringUtils.leftPad("abc", 5, 'x')); + assertEquals("\uffff\uffffabc", StringUtils.leftPad("abc", 5, '\uffff')); + assertEquals("abc", StringUtils.leftPad("abc", 2, ' ')); + String str = StringUtils.leftPad("aaa", 10000, 'a'); // bigger than pad length + assertEquals(10000, str.length()); + assertTrue(StringUtils.containsOnly(str, new char[] {'a'})); + } + + @Test + public void testLeftPad_StringIntString() { + assertEquals(null, StringUtils.leftPad(null, 5, "-+")); + assertEquals(null, StringUtils.leftPad(null, 5, null)); + assertEquals(" ", StringUtils.leftPad("", 5, " ")); + assertEquals("-+-+abc", StringUtils.leftPad("abc", 7, "-+")); + assertEquals("-+~abc", StringUtils.leftPad("abc", 6, "-+~")); + assertEquals("-+abc", StringUtils.leftPad("abc", 5, "-+~")); + assertEquals("abc", StringUtils.leftPad("abc", 2, " ")); + assertEquals("abc", StringUtils.leftPad("abc", -1, " ")); + assertEquals(" abc", StringUtils.leftPad("abc", 5, null)); + assertEquals(" abc", StringUtils.leftPad("abc", 5, "")); + } + + @Test + public void testLengthString() { + assertEquals(0, StringUtils.length(null)); + assertEquals(0, StringUtils.length("")); + assertEquals(0, StringUtils.length(StringUtils.EMPTY)); + assertEquals(1, StringUtils.length("A")); + assertEquals(1, StringUtils.length(" ")); + assertEquals(8, StringUtils.length("ABCDEFGH")); + } + + @Test + public void testLengthStringBuffer() { + assertEquals(0, StringUtils.length(new StringBuffer(""))); + assertEquals(0, StringUtils.length(new StringBuffer(StringUtils.EMPTY))); + assertEquals(1, StringUtils.length(new StringBuffer("A"))); + assertEquals(1, StringUtils.length(new StringBuffer(" "))); + assertEquals(8, StringUtils.length(new StringBuffer("ABCDEFGH"))); + } + + @Test + public void testLengthStringBuilder() { + assertEquals(0, StringUtils.length(new StringBuilder(""))); + assertEquals(0, StringUtils.length(new StringBuilder(StringUtils.EMPTY))); + assertEquals(1, StringUtils.length(new StringBuilder("A"))); + assertEquals(1, StringUtils.length(new StringBuilder(" "))); + assertEquals(8, StringUtils.length(new StringBuilder("ABCDEFGH"))); + } + + @Test + public void testLength_CharBuffer() { + assertEquals(0, StringUtils.length(CharBuffer.wrap(""))); + assertEquals(1, StringUtils.length(CharBuffer.wrap("A"))); + assertEquals(1, StringUtils.length(CharBuffer.wrap(" "))); + assertEquals(8, StringUtils.length(CharBuffer.wrap("ABCDEFGH"))); + } + + //----------------------------------------------------------------------- + @Test + public void testCenter_StringInt() { + assertEquals(null, StringUtils.center(null, -1)); + assertEquals(null, StringUtils.center(null, 4)); + assertEquals(" ", StringUtils.center("", 4)); + assertEquals("ab", StringUtils.center("ab", 0)); + assertEquals("ab", StringUtils.center("ab", -1)); + assertEquals("ab", StringUtils.center("ab", 1)); + assertEquals(" ", StringUtils.center("", 4)); + assertEquals(" ab ", StringUtils.center("ab", 4)); + assertEquals("abcd", StringUtils.center("abcd", 2)); + assertEquals(" a ", StringUtils.center("a", 4)); + assertEquals(" a ", StringUtils.center("a", 5)); + } + + @Test + public void testCenter_StringIntChar() { + assertEquals(null, StringUtils.center(null, -1, ' ')); + assertEquals(null, StringUtils.center(null, 4, ' ')); + assertEquals(" ", StringUtils.center("", 4, ' ')); + assertEquals("ab", StringUtils.center("ab", 0, ' ')); + assertEquals("ab", StringUtils.center("ab", -1, ' ')); + assertEquals("ab", StringUtils.center("ab", 1, ' ')); + assertEquals(" ", StringUtils.center("", 4, ' ')); + assertEquals(" ab ", StringUtils.center("ab", 4, ' ')); + assertEquals("abcd", StringUtils.center("abcd", 2, ' ')); + assertEquals(" a ", StringUtils.center("a", 4, ' ')); + assertEquals(" a ", StringUtils.center("a", 5, ' ')); + assertEquals("xxaxx", StringUtils.center("a", 5, 'x')); + } + + @Test + public void testCenter_StringIntString() { + assertEquals(null, StringUtils.center(null, 4, null)); + assertEquals(null, StringUtils.center(null, -1, " ")); + assertEquals(null, StringUtils.center(null, 4, " ")); + assertEquals(" ", StringUtils.center("", 4, " ")); + assertEquals("ab", StringUtils.center("ab", 0, " ")); + assertEquals("ab", StringUtils.center("ab", -1, " ")); + assertEquals("ab", StringUtils.center("ab", 1, " ")); + assertEquals(" ", StringUtils.center("", 4, " ")); + assertEquals(" ab ", StringUtils.center("ab", 4, " ")); + assertEquals("abcd", StringUtils.center("abcd", 2, " ")); + assertEquals(" a ", StringUtils.center("a", 4, " ")); + assertEquals("yayz", StringUtils.center("a", 4, "yz")); + assertEquals("yzyayzy", StringUtils.center("a", 7, "yz")); + assertEquals(" abc ", StringUtils.center("abc", 7, null)); + assertEquals(" abc ", StringUtils.center("abc", 7, "")); + } + + //----------------------------------------------------------------------- + @Test + public void testReverse_String() { + assertEquals(null, StringUtils.reverse(null) ); + assertEquals("", StringUtils.reverse("") ); + assertEquals("sdrawkcab", StringUtils.reverse("backwards") ); + } + + @Test + public void testReverseDelimited_StringChar() { + assertEquals(null, StringUtils.reverseDelimited(null, '.') ); + assertEquals("", StringUtils.reverseDelimited("", '.') ); + assertEquals("c.b.a", StringUtils.reverseDelimited("a.b.c", '.') ); + assertEquals("a b c", StringUtils.reverseDelimited("a b c", '.') ); + assertEquals("", StringUtils.reverseDelimited("", '.') ); + } + + //----------------------------------------------------------------------- + @Test + public void testDefault_String() { + assertEquals("", StringUtils.defaultString(null)); + assertEquals("", StringUtils.defaultString("")); + assertEquals("abc", StringUtils.defaultString("abc")); + } + + @Test + public void testDefault_StringString() { + assertEquals("NULL", StringUtils.defaultString(null, "NULL")); + assertEquals("", StringUtils.defaultString("", "NULL")); + assertEquals("abc", StringUtils.defaultString("abc", "NULL")); + } + + @Test + public void testDefaultIfEmpty_StringString() { + assertEquals("NULL", StringUtils.defaultIfEmpty(null, "NULL")); + assertEquals("NULL", StringUtils.defaultIfEmpty("", "NULL")); + assertEquals("abc", StringUtils.defaultIfEmpty("abc", "NULL")); + assertNull(StringUtils.defaultIfEmpty("", null)); + // Tests compatibility for the API return type + String s = StringUtils.defaultIfEmpty("abc", "NULL"); + assertEquals("abc", s); + } + + @Test + public void testDefaultIfBlank_StringString() { + assertEquals("NULL", StringUtils.defaultIfBlank(null, "NULL")); + assertEquals("NULL", StringUtils.defaultIfBlank("", "NULL")); + assertEquals("NULL", StringUtils.defaultIfBlank(" ", "NULL")); + assertEquals("abc", StringUtils.defaultIfBlank("abc", "NULL")); + assertNull(StringUtils.defaultIfBlank("", null)); + // Tests compatibility for the API return type + String s = StringUtils.defaultIfBlank("abc", "NULL"); + assertEquals("abc", s); + } + + @Test + public void testDefaultIfEmpty_StringBuilders() { + assertEquals("NULL", StringUtils.defaultIfEmpty(new StringBuilder(""), new StringBuilder("NULL")).toString()); + assertEquals("abc", StringUtils.defaultIfEmpty(new StringBuilder("abc"), new StringBuilder("NULL")).toString()); + assertNull(StringUtils.defaultIfEmpty(new StringBuilder(""), null)); + // Tests compatibility for the API return type + StringBuilder s = StringUtils.defaultIfEmpty(new StringBuilder("abc"), new StringBuilder("NULL")); + assertEquals("abc", s.toString()); + } + + @Test + public void testDefaultIfBlank_StringBuilders() { + assertEquals("NULL", StringUtils.defaultIfBlank(new StringBuilder(""), new StringBuilder("NULL")).toString()); + assertEquals("NULL", StringUtils.defaultIfBlank(new StringBuilder(" "), new StringBuilder("NULL")).toString()); + assertEquals("abc", StringUtils.defaultIfBlank(new StringBuilder("abc"), new StringBuilder("NULL")).toString()); + assertNull(StringUtils.defaultIfBlank(new StringBuilder(""), null)); + // Tests compatibility for the API return type + StringBuilder s = StringUtils.defaultIfBlank(new StringBuilder("abc"), new StringBuilder("NULL")); + assertEquals("abc", s.toString()); + } + + @Test + public void testDefaultIfEmpty_StringBuffers() { + assertEquals("NULL", StringUtils.defaultIfEmpty(new StringBuffer(""), new StringBuffer("NULL")).toString()); + assertEquals("abc", StringUtils.defaultIfEmpty(new StringBuffer("abc"), new StringBuffer("NULL")).toString()); + assertNull(StringUtils.defaultIfEmpty(new StringBuffer(""), null)); + // Tests compatibility for the API return type + StringBuffer s = StringUtils.defaultIfEmpty(new StringBuffer("abc"), new StringBuffer("NULL")); + assertEquals("abc", s.toString()); + } + + @Test + public void testDefaultIfBlank_StringBuffers() { + assertEquals("NULL", StringUtils.defaultIfBlank(new StringBuffer(""), new StringBuffer("NULL")).toString()); + assertEquals("NULL", StringUtils.defaultIfBlank(new StringBuffer(" "), new StringBuffer("NULL")).toString()); + assertEquals("abc", StringUtils.defaultIfBlank(new StringBuffer("abc"), new StringBuffer("NULL")).toString()); + assertNull(StringUtils.defaultIfBlank(new StringBuffer(""), null)); + // Tests compatibility for the API return type + StringBuffer s = StringUtils.defaultIfBlank(new StringBuffer("abc"), new StringBuffer("NULL")); + assertEquals("abc", s.toString()); + } + + @Test + public void testDefaultIfEmpty_CharBuffers() { + assertEquals("NULL", StringUtils.defaultIfEmpty(CharBuffer.wrap(""), CharBuffer.wrap("NULL")).toString()); + assertEquals("abc", StringUtils.defaultIfEmpty(CharBuffer.wrap("abc"), CharBuffer.wrap("NULL")).toString()); + assertNull(StringUtils.defaultIfEmpty(CharBuffer.wrap(""), null)); + // Tests compatibility for the API return type + CharBuffer s = StringUtils.defaultIfEmpty(CharBuffer.wrap("abc"), CharBuffer.wrap("NULL")); + assertEquals("abc", s.toString()); + } + + @Test + public void testDefaultIfBlank_CharBuffers() { + assertEquals("NULL", StringUtils.defaultIfBlank(CharBuffer.wrap(""), CharBuffer.wrap("NULL")).toString()); + assertEquals("NULL", StringUtils.defaultIfBlank(CharBuffer.wrap(" "), CharBuffer.wrap("NULL")).toString()); + assertEquals("abc", StringUtils.defaultIfBlank(CharBuffer.wrap("abc"), CharBuffer.wrap("NULL")).toString()); + assertNull(StringUtils.defaultIfBlank(CharBuffer.wrap(""), null)); + // Tests compatibility for the API return type + CharBuffer s = StringUtils.defaultIfBlank(CharBuffer.wrap("abc"), CharBuffer.wrap("NULL")); + assertEquals("abc", s.toString()); + } + + //----------------------------------------------------------------------- + @Test + public void testAbbreviate_StringInt() { + assertEquals(null, StringUtils.abbreviate(null, 10)); + assertEquals("", StringUtils.abbreviate("", 10)); + assertEquals("short", StringUtils.abbreviate("short", 10)); + assertEquals("Now is ...", StringUtils.abbreviate("Now is the time for all good men to come to the aid of their party.", 10)); + + String raspberry = "raspberry peach"; + assertEquals("raspberry p...", StringUtils.abbreviate(raspberry, 14)); + assertEquals("raspberry peach", StringUtils.abbreviate("raspberry peach", 15)); + assertEquals("raspberry peach", StringUtils.abbreviate("raspberry peach", 16)); + assertEquals("abc...", StringUtils.abbreviate("abcdefg", 6)); + assertEquals("abcdefg", StringUtils.abbreviate("abcdefg", 7)); + assertEquals("abcdefg", StringUtils.abbreviate("abcdefg", 8)); + assertEquals("a...", StringUtils.abbreviate("abcdefg", 4)); + assertEquals("", StringUtils.abbreviate("", 4)); + + try { + @SuppressWarnings("unused") + String res = StringUtils.abbreviate("abc", 3); + fail("StringUtils.abbreviate expecting IllegalArgumentException"); + } catch (IllegalArgumentException ex) { + // empty + } + } + + @Test + public void testAbbreviate_StringIntInt() { + assertEquals(null, StringUtils.abbreviate(null, 10, 12)); + assertEquals("", StringUtils.abbreviate("", 0, 10)); + assertEquals("", StringUtils.abbreviate("", 2, 10)); + + try { + @SuppressWarnings("unused") + String res = StringUtils.abbreviate("abcdefghij", 0, 3); + fail("StringUtils.abbreviate expecting IllegalArgumentException"); + } catch (IllegalArgumentException ex) { + // empty + } + try { + @SuppressWarnings("unused") + String res = StringUtils.abbreviate("abcdefghij", 5, 6); + fail("StringUtils.abbreviate expecting IllegalArgumentException"); + } catch (IllegalArgumentException ex) { + // empty + } + + + String raspberry = "raspberry peach"; + assertEquals("raspberry peach", StringUtils.abbreviate(raspberry, 11, 15)); + + assertEquals(null, StringUtils.abbreviate(null, 7, 14)); + assertAbbreviateWithOffset("abcdefg...", -1, 10); + assertAbbreviateWithOffset("abcdefg...", 0, 10); + assertAbbreviateWithOffset("abcdefg...", 1, 10); + assertAbbreviateWithOffset("abcdefg...", 2, 10); + assertAbbreviateWithOffset("abcdefg...", 3, 10); + assertAbbreviateWithOffset("abcdefg...", 4, 10); + assertAbbreviateWithOffset("...fghi...", 5, 10); + assertAbbreviateWithOffset("...ghij...", 6, 10); + assertAbbreviateWithOffset("...hijk...", 7, 10); + assertAbbreviateWithOffset("...ijklmno", 8, 10); + assertAbbreviateWithOffset("...ijklmno", 9, 10); + assertAbbreviateWithOffset("...ijklmno", 10, 10); + assertAbbreviateWithOffset("...ijklmno", 10, 10); + assertAbbreviateWithOffset("...ijklmno", 11, 10); + assertAbbreviateWithOffset("...ijklmno", 12, 10); + assertAbbreviateWithOffset("...ijklmno", 13, 10); + assertAbbreviateWithOffset("...ijklmno", 14, 10); + assertAbbreviateWithOffset("...ijklmno", 15, 10); + assertAbbreviateWithOffset("...ijklmno", 16, 10); + assertAbbreviateWithOffset("...ijklmno", Integer.MAX_VALUE, 10); + } + + private void assertAbbreviateWithOffset(String expected, int offset, int maxWidth) { + String abcdefghijklmno = "abcdefghijklmno"; + String message = "abbreviate(String,int,int) failed"; + String actual = StringUtils.abbreviate(abcdefghijklmno, offset, maxWidth); + if (offset >= 0 && offset < abcdefghijklmno.length()) { + assertTrue(message + " -- should contain offset character", + actual.indexOf((char)('a'+offset)) != -1); + } + assertTrue(message + " -- should not be greater than maxWidth", + actual.length() <= maxWidth); + assertEquals(message, expected, actual); + } + + @Test + public void testAbbreviateMiddle() { + // javadoc examples + assertNull( StringUtils.abbreviateMiddle(null, null, 0) ); + assertEquals( "abc", StringUtils.abbreviateMiddle("abc", null, 0) ); + assertEquals( "abc", StringUtils.abbreviateMiddle("abc", ".", 0) ); + assertEquals( "abc", StringUtils.abbreviateMiddle("abc", ".", 3) ); + assertEquals( "ab.f", StringUtils.abbreviateMiddle("abcdef", ".", 4) ); + + // JIRA issue (LANG-405) example (slightly different than actual expected result) + assertEquals( + "A very long text with un...f the text is complete.", + StringUtils.abbreviateMiddle( + "A very long text with unimportant stuff in the middle but interesting start and " + + "end to see if the text is complete.", "...", 50) ); + + // Test a much longer text :) + String longText = "Start text" + StringUtils.repeat("x", 10000) + "Close text"; + assertEquals( + "Start text->Close text", + StringUtils.abbreviateMiddle( longText, "->", 22 ) ); + + // Test negative length + assertEquals("abc", StringUtils.abbreviateMiddle("abc", ".", -1)); + + // Test boundaries + // Fails to change anything as method ensures first and last char are kept + assertEquals("abc", StringUtils.abbreviateMiddle("abc", ".", 1)); + assertEquals("abc", StringUtils.abbreviateMiddle("abc", ".", 2)); + + // Test length of n=1 + assertEquals("a", StringUtils.abbreviateMiddle("a", ".", 1)); + + // Test smallest length that can lead to success + assertEquals("a.d", StringUtils.abbreviateMiddle("abcd", ".", 3)); + + // More from LANG-405 + assertEquals("a..f", StringUtils.abbreviateMiddle("abcdef", "..", 4)); + assertEquals("ab.ef", StringUtils.abbreviateMiddle("abcdef", ".", 5)); + } + + //----------------------------------------------------------------------- + @Test + public void testDifference_StringString() { + assertEquals(null, StringUtils.difference(null, null)); + assertEquals("", StringUtils.difference("", "")); + assertEquals("abc", StringUtils.difference("", "abc")); + assertEquals("", StringUtils.difference("abc", "")); + assertEquals("i am a robot", StringUtils.difference(null, "i am a robot")); + assertEquals("i am a machine", StringUtils.difference("i am a machine", null)); + assertEquals("robot", StringUtils.difference("i am a machine", "i am a robot")); + assertEquals("", StringUtils.difference("abc", "abc")); + assertEquals("you are a robot", StringUtils.difference("i am a robot", "you are a robot")); + } + + @Test + public void testDifferenceAt_StringString() { + assertEquals(-1, StringUtils.indexOfDifference(null, null)); + assertEquals(0, StringUtils.indexOfDifference(null, "i am a robot")); + assertEquals(-1, StringUtils.indexOfDifference("", "")); + assertEquals(0, StringUtils.indexOfDifference("", "abc")); + assertEquals(0, StringUtils.indexOfDifference("abc", "")); + assertEquals(0, StringUtils.indexOfDifference("i am a machine", null)); + assertEquals(7, StringUtils.indexOfDifference("i am a machine", "i am a robot")); + assertEquals(-1, StringUtils.indexOfDifference("foo", "foo")); + assertEquals(0, StringUtils.indexOfDifference("i am a robot", "you are a robot")); + //System.out.println("indexOfDiff: " + StringUtils.indexOfDifference("i am a robot", "not machine")); + } + + //----------------------------------------------------------------------- + @Test + public void testGetLevenshteinDistance_StringString() { + assertEquals(0, StringUtils.getLevenshteinDistance("", "") ); + assertEquals(1, StringUtils.getLevenshteinDistance("", "a") ); + assertEquals(7, StringUtils.getLevenshteinDistance("aaapppp", "") ); + assertEquals(1, StringUtils.getLevenshteinDistance("frog", "fog") ); + assertEquals(3, StringUtils.getLevenshteinDistance("fly", "ant") ); + assertEquals(7, StringUtils.getLevenshteinDistance("elephant", "hippo") ); + assertEquals(7, StringUtils.getLevenshteinDistance("hippo", "elephant") ); + assertEquals(8, StringUtils.getLevenshteinDistance("hippo", "zzzzzzzz") ); + assertEquals(8, StringUtils.getLevenshteinDistance("zzzzzzzz", "hippo") ); + assertEquals(1, StringUtils.getLevenshteinDistance("hello", "hallo") ); + try { + @SuppressWarnings("unused") + int d = StringUtils.getLevenshteinDistance("a", null); + fail("expecting IllegalArgumentException"); + } catch (IllegalArgumentException ex) { + // empty + } + try { + @SuppressWarnings("unused") + int d = StringUtils.getLevenshteinDistance(null, "a"); + fail("expecting IllegalArgumentException"); + } catch (IllegalArgumentException ex) { + // empty + } + } + + @Test + public void testGetLevenshteinDistance_StringStringInt() { + // empty strings + assertEquals(0, StringUtils.getLevenshteinDistance("", "", 0)); + assertEquals(7, StringUtils.getLevenshteinDistance("aaapppp", "", 8)); + assertEquals(7, StringUtils.getLevenshteinDistance("aaapppp", "", 7)); + assertEquals(-1, StringUtils.getLevenshteinDistance("aaapppp", "", 6)); + + // unequal strings, zero threshold + assertEquals(-1, StringUtils.getLevenshteinDistance("b", "a", 0)); + assertEquals(-1, StringUtils.getLevenshteinDistance("a", "b", 0)); + + // equal strings + assertEquals(0, StringUtils.getLevenshteinDistance("aa", "aa", 0)); + assertEquals(0, StringUtils.getLevenshteinDistance("aa", "aa", 2)); + + // same length + assertEquals(-1, StringUtils.getLevenshteinDistance("aaa", "bbb", 2)); + assertEquals(3, StringUtils.getLevenshteinDistance("aaa", "bbb", 3)); + + // big stripe + assertEquals(6, StringUtils.getLevenshteinDistance("aaaaaa", "b", 10)); + + // distance less than threshold + assertEquals(7, StringUtils.getLevenshteinDistance("aaapppp", "b", 8)); + assertEquals(3, StringUtils.getLevenshteinDistance("a", "bbb", 4)); + + // distance equal to threshold + assertEquals(7, StringUtils.getLevenshteinDistance("aaapppp", "b", 7)); + assertEquals(3, StringUtils.getLevenshteinDistance("a", "bbb", 3)); + + // distance greater than threshold + assertEquals(-1, StringUtils.getLevenshteinDistance("a", "bbb", 2)); + assertEquals(-1, StringUtils.getLevenshteinDistance("bbb", "a", 2)); + assertEquals(-1, StringUtils.getLevenshteinDistance("aaapppp", "b", 6)); + + // stripe runs off array, strings not similar + assertEquals(-1, StringUtils.getLevenshteinDistance("a", "bbb", 1)); + assertEquals(-1, StringUtils.getLevenshteinDistance("bbb", "a", 1)); + + // stripe runs off array, strings are similar + assertEquals(-1, StringUtils.getLevenshteinDistance("12345", "1234567", 1)); + assertEquals(-1, StringUtils.getLevenshteinDistance("1234567", "12345", 1)); + + // old getLevenshteinDistance test cases + assertEquals(1, StringUtils.getLevenshteinDistance("frog", "fog",1) ); + assertEquals(3, StringUtils.getLevenshteinDistance("fly", "ant",3) ); + assertEquals(7, StringUtils.getLevenshteinDistance("elephant", "hippo",7) ); + assertEquals(-1, StringUtils.getLevenshteinDistance("elephant", "hippo",6) ); + assertEquals(7, StringUtils.getLevenshteinDistance("hippo", "elephant",7) ); + assertEquals(-1, StringUtils.getLevenshteinDistance("hippo", "elephant",6) ); + assertEquals(8, StringUtils.getLevenshteinDistance("hippo", "zzzzzzzz",8) ); + assertEquals(8, StringUtils.getLevenshteinDistance("zzzzzzzz", "hippo",8) ); + assertEquals(1, StringUtils.getLevenshteinDistance("hello", "hallo",1) ); + + // exceptions + try { + @SuppressWarnings("unused") + int d = StringUtils.getLevenshteinDistance("a", null, 0); + fail("expecting IllegalArgumentException"); + } catch (IllegalArgumentException ex) { + // empty + } + try { + @SuppressWarnings("unused") + int d = StringUtils.getLevenshteinDistance(null, "a", 0); + fail("expecting IllegalArgumentException"); + } catch (IllegalArgumentException ex) { + // empty + } + + try { + @SuppressWarnings("unused") + int d = StringUtils.getLevenshteinDistance("a", "a", -1); + fail("expecting IllegalArgumentException"); + } catch (IllegalArgumentException ex) { + // empty + } + } + + /** + * A sanity check for {@link StringUtils#EMPTY}. + */ + @Test + public void testEMPTY() { + assertNotNull(StringUtils.EMPTY); + assertEquals("", StringUtils.EMPTY); + assertEquals(0, StringUtils.EMPTY.length()); + } + + /** + * Test for {@link StringUtils#isAllLowerCase(CharSequence)}. + */ + @Test + public void testIsAllLowerCase() { + assertFalse(StringUtils.isAllLowerCase(null)); + assertFalse(StringUtils.isAllLowerCase(StringUtils.EMPTY)); + assertTrue(StringUtils.isAllLowerCase("abc")); + assertFalse(StringUtils.isAllLowerCase("abc ")); + assertFalse(StringUtils.isAllLowerCase("abC")); + } + + /** + * Test for {@link StringUtils#isAllUpperCase(CharSequence)}. + */ + @Test + public void testIsAllUpperCase() { + assertFalse(StringUtils.isAllUpperCase(null)); + assertFalse(StringUtils.isAllUpperCase(StringUtils.EMPTY)); + assertTrue(StringUtils.isAllUpperCase("ABC")); + assertFalse(StringUtils.isAllUpperCase("ABC ")); + assertFalse(StringUtils.isAllUpperCase("aBC")); + } + + @Test + public void testRemoveStart() { + // StringUtils.removeStart("", *) = "" + assertNull(StringUtils.removeStart(null, null)); + assertNull(StringUtils.removeStart(null, "")); + assertNull(StringUtils.removeStart(null, "a")); + + // StringUtils.removeStart(*, null) = * + assertEquals(StringUtils.removeStart("", null), ""); + assertEquals(StringUtils.removeStart("", ""), ""); + assertEquals(StringUtils.removeStart("", "a"), ""); + + // All others: + assertEquals(StringUtils.removeStart("www.domain.com", "www."), "domain.com"); + assertEquals(StringUtils.removeStart("domain.com", "www."), "domain.com"); + assertEquals(StringUtils.removeStart("domain.com", ""), "domain.com"); + assertEquals(StringUtils.removeStart("domain.com", null), "domain.com"); + } + + @Test + public void testRemoveStartIgnoreCase() { + // StringUtils.removeStart("", *) = "" + assertNull("removeStartIgnoreCase(null, null)", StringUtils.removeStartIgnoreCase(null, null)); + assertNull("removeStartIgnoreCase(null, \"\")", StringUtils.removeStartIgnoreCase(null, "")); + assertNull("removeStartIgnoreCase(null, \"a\")", StringUtils.removeStartIgnoreCase(null, "a")); + + // StringUtils.removeStart(*, null) = * + assertEquals("removeStartIgnoreCase(\"\", null)", StringUtils.removeStartIgnoreCase("", null), ""); + assertEquals("removeStartIgnoreCase(\"\", \"\")", StringUtils.removeStartIgnoreCase("", ""), ""); + assertEquals("removeStartIgnoreCase(\"\", \"a\")", StringUtils.removeStartIgnoreCase("", "a"), ""); + + // All others: + assertEquals("removeStartIgnoreCase(\"www.domain.com\", \"www.\")", StringUtils.removeStartIgnoreCase("www.domain.com", "www."), "domain.com"); + assertEquals("removeStartIgnoreCase(\"domain.com\", \"www.\")", StringUtils.removeStartIgnoreCase("domain.com", "www."), "domain.com"); + assertEquals("removeStartIgnoreCase(\"domain.com\", \"\")", StringUtils.removeStartIgnoreCase("domain.com", ""), "domain.com"); + assertEquals("removeStartIgnoreCase(\"domain.com\", null)", StringUtils.removeStartIgnoreCase("domain.com", null), "domain.com"); + + // Case insensitive: + assertEquals("removeStartIgnoreCase(\"www.domain.com\", \"WWW.\")", StringUtils.removeStartIgnoreCase("www.domain.com", "WWW."), "domain.com"); + } + + @Test + public void testRemoveEnd() { + // StringUtils.removeEnd("", *) = "" + assertNull(StringUtils.removeEnd(null, null)); + assertNull(StringUtils.removeEnd(null, "")); + assertNull(StringUtils.removeEnd(null, "a")); + + // StringUtils.removeEnd(*, null) = * + assertEquals(StringUtils.removeEnd("", null), ""); + assertEquals(StringUtils.removeEnd("", ""), ""); + assertEquals(StringUtils.removeEnd("", "a"), ""); + + // All others: + assertEquals(StringUtils.removeEnd("www.domain.com.", ".com"), "www.domain.com."); + assertEquals(StringUtils.removeEnd("www.domain.com", ".com"), "www.domain"); + assertEquals(StringUtils.removeEnd("www.domain", ".com"), "www.domain"); + assertEquals(StringUtils.removeEnd("domain.com", ""), "domain.com"); + assertEquals(StringUtils.removeEnd("domain.com", null), "domain.com"); + } + + @Test + public void testRemoveEndIgnoreCase() { + // StringUtils.removeEndIgnoreCase("", *) = "" + assertNull("removeEndIgnoreCase(null, null)", StringUtils.removeEndIgnoreCase(null, null)); + assertNull("removeEndIgnoreCase(null, \"\")", StringUtils.removeEndIgnoreCase(null, "")); + assertNull("removeEndIgnoreCase(null, \"a\")", StringUtils.removeEndIgnoreCase(null, "a")); + + // StringUtils.removeEnd(*, null) = * + assertEquals("removeEndIgnoreCase(\"\", null)", StringUtils.removeEndIgnoreCase("", null), ""); + assertEquals("removeEndIgnoreCase(\"\", \"\")", StringUtils.removeEndIgnoreCase("", ""), ""); + assertEquals("removeEndIgnoreCase(\"\", \"a\")", StringUtils.removeEndIgnoreCase("", "a"), ""); + + // All others: + assertEquals("removeEndIgnoreCase(\"www.domain.com.\", \".com\")", StringUtils.removeEndIgnoreCase("www.domain.com.", ".com"), "www.domain.com."); + assertEquals("removeEndIgnoreCase(\"www.domain.com\", \".com\")", StringUtils.removeEndIgnoreCase("www.domain.com", ".com"), "www.domain"); + assertEquals("removeEndIgnoreCase(\"www.domain\", \".com\")", StringUtils.removeEndIgnoreCase("www.domain", ".com"), "www.domain"); + assertEquals("removeEndIgnoreCase(\"domain.com\", \"\")", StringUtils.removeEndIgnoreCase("domain.com", ""), "domain.com"); + assertEquals("removeEndIgnoreCase(\"domain.com\", null)", StringUtils.removeEndIgnoreCase("domain.com", null), "domain.com"); + + // Case insensitive: + assertEquals("removeEndIgnoreCase(\"www.domain.com\", \".COM\")", StringUtils.removeEndIgnoreCase("www.domain.com", ".COM"), "www.domain"); + assertEquals("removeEndIgnoreCase(\"www.domain.COM\", \".com\")", StringUtils.removeEndIgnoreCase("www.domain.COM", ".com"), "www.domain"); + } + + @Test + public void testRemove_String() { + // StringUtils.remove(null, *) = null + assertEquals(null, StringUtils.remove(null, null)); + assertEquals(null, StringUtils.remove(null, "")); + assertEquals(null, StringUtils.remove(null, "a")); + + // StringUtils.remove("", *) = "" + assertEquals("", StringUtils.remove("", null)); + assertEquals("", StringUtils.remove("", "")); + assertEquals("", StringUtils.remove("", "a")); + + // StringUtils.remove(*, null) = * + assertEquals(null, StringUtils.remove(null, null)); + assertEquals("", StringUtils.remove("", null)); + assertEquals("a", StringUtils.remove("a", null)); + + // StringUtils.remove(*, "") = * + assertEquals(null, StringUtils.remove(null, "")); + assertEquals("", StringUtils.remove("", "")); + assertEquals("a", StringUtils.remove("a", "")); + + // StringUtils.remove("queued", "ue") = "qd" + assertEquals("qd", StringUtils.remove("queued", "ue")); + + // StringUtils.remove("queued", "zz") = "queued" + assertEquals("queued", StringUtils.remove("queued", "zz")); + } + + @Test + public void testRemove_char() { + // StringUtils.remove(null, *) = null + assertEquals(null, StringUtils.remove(null, 'a')); + assertEquals(null, StringUtils.remove(null, 'a')); + assertEquals(null, StringUtils.remove(null, 'a')); + + // StringUtils.remove("", *) = "" + assertEquals("", StringUtils.remove("", 'a')); + assertEquals("", StringUtils.remove("", 'a')); + assertEquals("", StringUtils.remove("", 'a')); + + // StringUtils.remove("queued", 'u') = "qeed" + assertEquals("qeed", StringUtils.remove("queued", 'u')); + + // StringUtils.remove("queued", 'z') = "queued" + assertEquals("queued", StringUtils.remove("queued", 'z')); + } + + @Test + public void testDifferenceAt_StringArray() { + assertEquals(-1, StringUtils.indexOfDifference((String[])null)); + assertEquals(-1, StringUtils.indexOfDifference(new String[] {})); + assertEquals(-1, StringUtils.indexOfDifference(new String[] {"abc"})); + assertEquals(-1, StringUtils.indexOfDifference(new String[] {null, null})); + assertEquals(-1, StringUtils.indexOfDifference(new String[] {"", ""})); + assertEquals(0, StringUtils.indexOfDifference(new String[] {"", null})); + assertEquals(0, StringUtils.indexOfDifference(new String[] {"abc", null, null})); + assertEquals(0, StringUtils.indexOfDifference(new String[] {null, null, "abc"})); + assertEquals(0, StringUtils.indexOfDifference(new String[] {"", "abc"})); + assertEquals(0, StringUtils.indexOfDifference(new String[] {"abc", ""})); + assertEquals(-1, StringUtils.indexOfDifference(new String[] {"abc", "abc"})); + assertEquals(1, StringUtils.indexOfDifference(new String[] {"abc", "a"})); + assertEquals(2, StringUtils.indexOfDifference(new String[] {"ab", "abxyz"})); + assertEquals(2, StringUtils.indexOfDifference(new String[] {"abcde", "abxyz"})); + assertEquals(0, StringUtils.indexOfDifference(new String[] {"abcde", "xyz"})); + assertEquals(0, StringUtils.indexOfDifference(new String[] {"xyz", "abcde"})); + assertEquals(7, StringUtils.indexOfDifference(new String[] {"i am a machine", "i am a robot"})); + } + + @Test + public void testGetCommonPrefix_StringArray() { + assertEquals("", StringUtils.getCommonPrefix((String[])null)); + assertEquals("", StringUtils.getCommonPrefix()); + assertEquals("abc", StringUtils.getCommonPrefix("abc")); + assertEquals("", StringUtils.getCommonPrefix(null, null)); + assertEquals("", StringUtils.getCommonPrefix("", "")); + assertEquals("", StringUtils.getCommonPrefix("", null)); + assertEquals("", StringUtils.getCommonPrefix("abc", null, null)); + assertEquals("", StringUtils.getCommonPrefix(null, null, "abc")); + assertEquals("", StringUtils.getCommonPrefix("", "abc")); + assertEquals("", StringUtils.getCommonPrefix("abc", "")); + assertEquals("abc", StringUtils.getCommonPrefix("abc", "abc")); + assertEquals("a", StringUtils.getCommonPrefix("abc", "a")); + assertEquals("ab", StringUtils.getCommonPrefix("ab", "abxyz")); + assertEquals("ab", StringUtils.getCommonPrefix("abcde", "abxyz")); + assertEquals("", StringUtils.getCommonPrefix("abcde", "xyz")); + assertEquals("", StringUtils.getCommonPrefix("xyz", "abcde")); + assertEquals("i am a ", StringUtils.getCommonPrefix("i am a machine", "i am a robot")); + } + + @Test + public void testNormalizeSpace() { + assertEquals(null, StringUtils.normalizeSpace(null)); + assertEquals("", StringUtils.normalizeSpace("")); + assertEquals("", StringUtils.normalizeSpace(" ")); + assertEquals("", StringUtils.normalizeSpace("\t")); + assertEquals("", StringUtils.normalizeSpace("\n")); + assertEquals("", StringUtils.normalizeSpace("\u0009")); + assertEquals("", StringUtils.normalizeSpace("\u000B")); + assertEquals("", StringUtils.normalizeSpace("\u000C")); + assertEquals("", StringUtils.normalizeSpace("\u001C")); + assertEquals("", StringUtils.normalizeSpace("\u001D")); + assertEquals("", StringUtils.normalizeSpace("\u001E")); + assertEquals("", StringUtils.normalizeSpace("\u001F")); + assertEquals("", StringUtils.normalizeSpace("\f")); + assertEquals("", StringUtils.normalizeSpace("\r")); + assertEquals("a", StringUtils.normalizeSpace(" a ")); + assertEquals("a b c", StringUtils.normalizeSpace(" a b c ")); + assertEquals("a b c", StringUtils.normalizeSpace("a\t\f\r b\u000B c\n")); + } + + @Test + public void testLANG666() { + assertEquals("12",StringUtils.stripEnd("120.00", ".0")); + assertEquals("121",StringUtils.stripEnd("121.00", ".0")); + } + + // Methods on StringUtils that are immutable in spirit (i.e. calculate the length) + // should take a CharSequence parameter. Methods that are mutable in spirit (i.e. capitalize) + // should take a String or String[] parameter and return String or String[]. + // This test enforces that this is done. + @Test + public void testStringUtilsCharSequenceContract() { + Class c = StringUtils.class; + Method[] methods = c.getMethods(); + for (Method m : methods) { + if (m.getReturnType() == String.class || m.getReturnType() == String[].class) { + // Assume this is mutable and ensure the first parameter is not CharSequence. + // It may be String or it may be something else (String[], Object, Object[]) so + // don't actively test for that. + Class[] params = m.getParameterTypes(); + if ( params.length > 0 && (params[0] == CharSequence.class || params[0] == CharSequence[].class)) { + fail("The method " + m + " appears to be mutable in spirit and therefore must not accept a CharSequence"); + } + } else { + // Assume this is immutable in spirit and ensure the first parameter is not String. + // As above, it may be something other than CharSequence. + Class[] params = m.getParameterTypes(); + if ( params.length > 0 && (params[0] == String.class || params[0] == String[].class)) { + fail("The method " + m + " appears to be immutable in spirit and therefore must not accept a String"); + } + } + } + } + + /** + * Tests {@link StringUtils#toString(byte[], String)} + * + * @throws UnsupportedEncodingException + * @see StringUtils#toString(byte[], String) + */ + @Test + public void testToString() throws UnsupportedEncodingException { + final String expectedString = "The quick brown fox jumped over the lazy dog."; + String encoding = SystemUtils.FILE_ENCODING; + byte[] expectedBytes = expectedString.getBytes(encoding); + // sanity check start + assertArrayEquals(expectedBytes, expectedString.getBytes()); + // sanity check end + assertEquals(expectedString, StringUtils.toString(expectedBytes, null)); + assertEquals(expectedString, StringUtils.toString(expectedBytes, encoding)); + encoding = "UTF-16"; + expectedBytes = expectedString.getBytes(encoding); + assertEquals(expectedString, StringUtils.toString(expectedBytes, encoding)); + } +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/StringUtilsTrimEmptyTest.java b/ApacheCommonsLang/org/apache/commons/lang3/StringUtilsTrimEmptyTest.java new file mode 100644 index 0000000..624afae --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/StringUtilsTrimEmptyTest.java @@ -0,0 +1,283 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3; + +import junit.framework.TestCase; + +/** + * Unit tests {@link org.apache.commons.lang3.StringUtils} - Trim/Empty methods + * + * @version $Id: StringUtilsTrimEmptyTest.java 1132845 2011-06-07 00:50:50Z sebb $ + */ +public class StringUtilsTrimEmptyTest extends TestCase { + private static final String FOO = "foo"; + + public StringUtilsTrimEmptyTest(String name) { + super(name); + } + + //----------------------------------------------------------------------- + public void testIsEmpty() { + assertEquals(true, StringUtils.isEmpty(null)); + assertEquals(true, StringUtils.isEmpty("")); + assertEquals(false, StringUtils.isEmpty(" ")); + assertEquals(false, StringUtils.isEmpty("foo")); + assertEquals(false, StringUtils.isEmpty(" foo ")); + } + + public void testIsNotEmpty() { + assertEquals(false, StringUtils.isNotEmpty(null)); + assertEquals(false, StringUtils.isNotEmpty("")); + assertEquals(true, StringUtils.isNotEmpty(" ")); + assertEquals(true, StringUtils.isNotEmpty("foo")); + assertEquals(true, StringUtils.isNotEmpty(" foo ")); + } + + public void testIsBlank() { + assertEquals(true, StringUtils.isBlank(null)); + assertEquals(true, StringUtils.isBlank("")); + assertEquals(true, StringUtils.isBlank(StringUtilsTest.WHITESPACE)); + assertEquals(false, StringUtils.isBlank("foo")); + assertEquals(false, StringUtils.isBlank(" foo ")); + } + + public void testIsNotBlank() { + assertEquals(false, StringUtils.isNotBlank(null)); + assertEquals(false, StringUtils.isNotBlank("")); + assertEquals(false, StringUtils.isNotBlank(StringUtilsTest.WHITESPACE)); + assertEquals(true, StringUtils.isNotBlank("foo")); + assertEquals(true, StringUtils.isNotBlank(" foo ")); + } + + //----------------------------------------------------------------------- + public void testTrim() { + assertEquals(FOO, StringUtils.trim(FOO + " ")); + assertEquals(FOO, StringUtils.trim(" " + FOO + " ")); + assertEquals(FOO, StringUtils.trim(" " + FOO)); + assertEquals(FOO, StringUtils.trim(FOO + "")); + assertEquals("", StringUtils.trim(" \t\r\n\b ")); + assertEquals("", StringUtils.trim(StringUtilsTest.TRIMMABLE)); + assertEquals(StringUtilsTest.NON_TRIMMABLE, StringUtils.trim(StringUtilsTest.NON_TRIMMABLE)); + assertEquals("", StringUtils.trim("")); + assertEquals(null, StringUtils.trim(null)); + } + + public void testTrimToNull() { + assertEquals(FOO, StringUtils.trimToNull(FOO + " ")); + assertEquals(FOO, StringUtils.trimToNull(" " + FOO + " ")); + assertEquals(FOO, StringUtils.trimToNull(" " + FOO)); + assertEquals(FOO, StringUtils.trimToNull(FOO + "")); + assertEquals(null, StringUtils.trimToNull(" \t\r\n\b ")); + assertEquals(null, StringUtils.trimToNull(StringUtilsTest.TRIMMABLE)); + assertEquals(StringUtilsTest.NON_TRIMMABLE, StringUtils.trimToNull(StringUtilsTest.NON_TRIMMABLE)); + assertEquals(null, StringUtils.trimToNull("")); + assertEquals(null, StringUtils.trimToNull(null)); + } + + public void testTrimToEmpty() { + assertEquals(FOO, StringUtils.trimToEmpty(FOO + " ")); + assertEquals(FOO, StringUtils.trimToEmpty(" " + FOO + " ")); + assertEquals(FOO, StringUtils.trimToEmpty(" " + FOO)); + assertEquals(FOO, StringUtils.trimToEmpty(FOO + "")); + assertEquals("", StringUtils.trimToEmpty(" \t\r\n\b ")); + assertEquals("", StringUtils.trimToEmpty(StringUtilsTest.TRIMMABLE)); + assertEquals(StringUtilsTest.NON_TRIMMABLE, StringUtils.trimToEmpty(StringUtilsTest.NON_TRIMMABLE)); + assertEquals("", StringUtils.trimToEmpty("")); + assertEquals("", StringUtils.trimToEmpty(null)); + } + + //----------------------------------------------------------------------- + public void testStrip_String() { + assertEquals(null, StringUtils.strip(null)); + assertEquals("", StringUtils.strip("")); + assertEquals("", StringUtils.strip(" ")); + assertEquals("abc", StringUtils.strip(" abc ")); + assertEquals(StringUtilsTest.NON_WHITESPACE, + StringUtils.strip(StringUtilsTest.WHITESPACE + StringUtilsTest.NON_WHITESPACE + StringUtilsTest.WHITESPACE)); + } + + public void testStripToNull_String() { + assertEquals(null, StringUtils.stripToNull(null)); + assertEquals(null, StringUtils.stripToNull("")); + assertEquals(null, StringUtils.stripToNull(" ")); + assertEquals(null, StringUtils.stripToNull(StringUtilsTest.WHITESPACE)); + assertEquals("ab c", StringUtils.stripToNull(" ab c ")); + assertEquals(StringUtilsTest.NON_WHITESPACE, + StringUtils.stripToNull(StringUtilsTest.WHITESPACE + StringUtilsTest.NON_WHITESPACE + StringUtilsTest.WHITESPACE)); + } + + public void testStripToEmpty_String() { + assertEquals("", StringUtils.stripToEmpty(null)); + assertEquals("", StringUtils.stripToEmpty("")); + assertEquals("", StringUtils.stripToEmpty(" ")); + assertEquals("", StringUtils.stripToEmpty(StringUtilsTest.WHITESPACE)); + assertEquals("ab c", StringUtils.stripToEmpty(" ab c ")); + assertEquals(StringUtilsTest.NON_WHITESPACE, + StringUtils.stripToEmpty(StringUtilsTest.WHITESPACE + StringUtilsTest.NON_WHITESPACE + StringUtilsTest.WHITESPACE)); + } + + public void testStrip_StringString() { + // null strip + assertEquals(null, StringUtils.strip(null, null)); + assertEquals("", StringUtils.strip("", null)); + assertEquals("", StringUtils.strip(" ", null)); + assertEquals("abc", StringUtils.strip(" abc ", null)); + assertEquals(StringUtilsTest.NON_WHITESPACE, + StringUtils.strip(StringUtilsTest.WHITESPACE + StringUtilsTest.NON_WHITESPACE + StringUtilsTest.WHITESPACE, null)); + + // "" strip + assertEquals(null, StringUtils.strip(null, "")); + assertEquals("", StringUtils.strip("", "")); + assertEquals(" ", StringUtils.strip(" ", "")); + assertEquals(" abc ", StringUtils.strip(" abc ", "")); + assertEquals(StringUtilsTest.WHITESPACE, StringUtils.strip(StringUtilsTest.WHITESPACE, "")); + + // " " strip + assertEquals(null, StringUtils.strip(null, " ")); + assertEquals("", StringUtils.strip("", " ")); + assertEquals("", StringUtils.strip(" ", " ")); + assertEquals("abc", StringUtils.strip(" abc ", " ")); + + // "ab" strip + assertEquals(null, StringUtils.strip(null, "ab")); + assertEquals("", StringUtils.strip("", "ab")); + assertEquals(" ", StringUtils.strip(" ", "ab")); + assertEquals(" abc ", StringUtils.strip(" abc ", "ab")); + assertEquals("c", StringUtils.strip("abcabab", "ab")); + assertEquals(StringUtilsTest.WHITESPACE, StringUtils.strip(StringUtilsTest.WHITESPACE, "")); + } + + public void testStripStart_StringString() { + // null stripStart + assertEquals(null, StringUtils.stripStart(null, null)); + assertEquals("", StringUtils.stripStart("", null)); + assertEquals("", StringUtils.stripStart(" ", null)); + assertEquals("abc ", StringUtils.stripStart(" abc ", null)); + assertEquals(StringUtilsTest.NON_WHITESPACE + StringUtilsTest.WHITESPACE, + StringUtils.stripStart(StringUtilsTest.WHITESPACE + StringUtilsTest.NON_WHITESPACE + StringUtilsTest.WHITESPACE, null)); + + // "" stripStart + assertEquals(null, StringUtils.stripStart(null, "")); + assertEquals("", StringUtils.stripStart("", "")); + assertEquals(" ", StringUtils.stripStart(" ", "")); + assertEquals(" abc ", StringUtils.stripStart(" abc ", "")); + assertEquals(StringUtilsTest.WHITESPACE, StringUtils.stripStart(StringUtilsTest.WHITESPACE, "")); + + // " " stripStart + assertEquals(null, StringUtils.stripStart(null, " ")); + assertEquals("", StringUtils.stripStart("", " ")); + assertEquals("", StringUtils.stripStart(" ", " ")); + assertEquals("abc ", StringUtils.stripStart(" abc ", " ")); + + // "ab" stripStart + assertEquals(null, StringUtils.stripStart(null, "ab")); + assertEquals("", StringUtils.stripStart("", "ab")); + assertEquals(" ", StringUtils.stripStart(" ", "ab")); + assertEquals(" abc ", StringUtils.stripStart(" abc ", "ab")); + assertEquals("cabab", StringUtils.stripStart("abcabab", "ab")); + assertEquals(StringUtilsTest.WHITESPACE, StringUtils.stripStart(StringUtilsTest.WHITESPACE, "")); + } + + public void testStripEnd_StringString() { + // null stripEnd + assertEquals(null, StringUtils.stripEnd(null, null)); + assertEquals("", StringUtils.stripEnd("", null)); + assertEquals("", StringUtils.stripEnd(" ", null)); + assertEquals(" abc", StringUtils.stripEnd(" abc ", null)); + assertEquals(StringUtilsTest.WHITESPACE + StringUtilsTest.NON_WHITESPACE, + StringUtils.stripEnd(StringUtilsTest.WHITESPACE + StringUtilsTest.NON_WHITESPACE + StringUtilsTest.WHITESPACE, null)); + + // "" stripEnd + assertEquals(null, StringUtils.stripEnd(null, "")); + assertEquals("", StringUtils.stripEnd("", "")); + assertEquals(" ", StringUtils.stripEnd(" ", "")); + assertEquals(" abc ", StringUtils.stripEnd(" abc ", "")); + assertEquals(StringUtilsTest.WHITESPACE, StringUtils.stripEnd(StringUtilsTest.WHITESPACE, "")); + + // " " stripEnd + assertEquals(null, StringUtils.stripEnd(null, " ")); + assertEquals("", StringUtils.stripEnd("", " ")); + assertEquals("", StringUtils.stripEnd(" ", " ")); + assertEquals(" abc", StringUtils.stripEnd(" abc ", " ")); + + // "ab" stripEnd + assertEquals(null, StringUtils.stripEnd(null, "ab")); + assertEquals("", StringUtils.stripEnd("", "ab")); + assertEquals(" ", StringUtils.stripEnd(" ", "ab")); + assertEquals(" abc ", StringUtils.stripEnd(" abc ", "ab")); + assertEquals("abc", StringUtils.stripEnd("abcabab", "ab")); + assertEquals(StringUtilsTest.WHITESPACE, StringUtils.stripEnd(StringUtilsTest.WHITESPACE, "")); + } + + public void testStripAll() { + // test stripAll method, merely an array version of the above strip + String[] empty = new String[0]; + String[] fooSpace = new String[] { " "+FOO+" ", " "+FOO, FOO+" " }; + String[] fooDots = new String[] { ".."+FOO+"..", ".."+FOO, FOO+".." }; + String[] foo = new String[] { FOO, FOO, FOO }; + +// assertEquals(null, StringUtils.stripAll(null)); // generates warning + assertEquals(null, StringUtils.stripAll((String[]) null)); // equivalent explicit cast + // Additional varargs tests + assertArrayEquals(empty, StringUtils.stripAll()); // empty array + assertArrayEquals(new String[]{null}, StringUtils.stripAll((String) null)); // == new String[]{null} + + assertArrayEquals(empty, StringUtils.stripAll(empty)); + assertArrayEquals(foo, StringUtils.stripAll(fooSpace)); + + assertEquals(null, StringUtils.stripAll(null, null)); + assertArrayEquals(foo, StringUtils.stripAll(fooSpace, null)); + assertArrayEquals(foo, StringUtils.stripAll(fooDots, ".")); + } + + public void testStripAccents() { + String cue = "\u00C7\u00FA\u00EA"; + assertEquals( "Failed to strip accents from " + cue, "Cue", StringUtils.stripAccents(cue)); + + String lots = "\u00C0\u00C1\u00C2\u00C3\u00C4\u00C5\u00C7\u00C8\u00C9" + + "\u00CA\u00CB\u00CC\u00CD\u00CE\u00CF\u00D1\u00D2\u00D3" + + "\u00D4\u00D5\u00D6\u00D9\u00DA\u00DB\u00DC\u00DD"; + assertEquals( "Failed to strip accents from " + lots, + "AAAAAACEEEEIIIINOOOOOUUUUY", + StringUtils.stripAccents(lots)); + + assertNull( "Failed null safety", StringUtils.stripAccents(null) ); + assertEquals( "Failed empty String", "", StringUtils.stripAccents("") ); + assertEquals( "Failed to handle non-accented text", "control", StringUtils.stripAccents("control") ); + assertEquals( "Failed to handle easy example", "eclair", StringUtils.stripAccents("\u00E9clair") ); + } + + //----------------------------------------------------------------------- + + private void assertArrayEquals(Object[] o1, Object[] o2) { + if(o1 == null) { + assertEquals(o1,o2); + return; + } + assertEquals("Length not equal. ", o1.length, o2.length); + int sz = o1.length; + for(int i=0; i + * Helpers for {@code java.lang.System}. + *

+ *

+ * If a system property cannot be read due to security restrictions, the corresponding field in this class will be set + * to {@code null} and a message will be written to {@code System.err}. + *

+ *

+ * #ThreadSafe# + *

+ * + * @since 1.0 + * @version $Id: SystemUtils.java 1199816 2011-11-09 16:11:34Z bayard $ + */ +public class SystemUtils { + + /** + * The prefix String for all Windows OS. + */ + private static final String OS_NAME_WINDOWS_PREFIX = "Windows"; + + // System property constants + // ----------------------------------------------------------------------- + // These MUST be declared first. Other constants depend on this. + + /** + * The System property key for the user home directory. + */ + private static final String USER_HOME_KEY = "user.home"; + + /** + * The System property key for the user directory. + */ + private static final String USER_DIR_KEY = "user.dir"; + + /** + * The System property key for the Java IO temporary directory. + */ + private static final String JAVA_IO_TMPDIR_KEY = "java.io.tmpdir"; + + /** + * The System property key for the Java home directory. + */ + private static final String JAVA_HOME_KEY = "java.home"; + + /** + *

+ * The {@code awt.toolkit} System Property. + *

+ *

+ * Holds a class name, on Windows XP this is {@code sun.awt.windows.WToolkit}. + *

+ *

+ * On platforms without a GUI, this value is {@code null}. + *

+ *

+ * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + *

+ *

+ * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + *

+ * + * @since 2.1 + */ + public static final String AWT_TOOLKIT = getSystemProperty("awt.toolkit"); + + /** + *

+ * The {@code file.encoding} System Property. + *

+ *

+ * File encoding, such as {@code Cp1252}. + *

+ *

+ * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + *

+ *

+ * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + *

+ * + * @since 2.0 + * @since Java 1.2 + */ + public static final String FILE_ENCODING = getSystemProperty("file.encoding"); + + /** + *

+ * The {@code file.separator} System Property. File separator ("/" on UNIX). + *

+ *

+ * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + *

+ *

+ * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + *

+ * + * @since Java 1.1 + */ + public static final String FILE_SEPARATOR = getSystemProperty("file.separator"); + + /** + *

+ * The {@code java.awt.fonts} System Property. + *

+ *

+ * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + *

+ *

+ * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + *

+ * + * @since 2.1 + */ + public static final String JAVA_AWT_FONTS = getSystemProperty("java.awt.fonts"); + + /** + *

+ * The {@code java.awt.graphicsenv} System Property. + *

+ *

+ * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + *

+ *

+ * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + *

+ * + * @since 2.1 + */ + public static final String JAVA_AWT_GRAPHICSENV = getSystemProperty("java.awt.graphicsenv"); + + /** + *

+ * The {@code java.awt.headless} System Property. The value of this property is the String {@code "true"} or + * {@code "false"}. + *

+ *

+ * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + *

+ *

+ * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + *

+ * + * @see #isJavaAwtHeadless() + * @since 2.1 + * @since Java 1.4 + */ + public static final String JAVA_AWT_HEADLESS = getSystemProperty("java.awt.headless"); + + /** + *

+ * The {@code java.awt.printerjob} System Property. + *

+ *

+ * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + *

+ *

+ * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + *

+ * + * @since 2.1 + */ + public static final String JAVA_AWT_PRINTERJOB = getSystemProperty("java.awt.printerjob"); + + /** + *

+ * The {@code java.class.path} System Property. Java class path. + *

+ *

+ * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + *

+ *

+ * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + *

+ * + * @since Java 1.1 + */ + public static final String JAVA_CLASS_PATH = getSystemProperty("java.class.path"); + + /** + *

+ * The {@code java.class.version} System Property. Java class format version number. + *

+ *

+ * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + *

+ *

+ * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + *

+ * + * @since Java 1.1 + */ + public static final String JAVA_CLASS_VERSION = getSystemProperty("java.class.version"); + + /** + *

+ * The {@code java.compiler} System Property. Name of JIT compiler to use. First in JDK version 1.2. Not used in Sun + * JDKs after 1.2. + *

+ *

+ * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + *

+ *

+ * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + *

+ * + * @since Java 1.2. Not used in Sun versions after 1.2. + */ + public static final String JAVA_COMPILER = getSystemProperty("java.compiler"); + + /** + *

+ * The {@code java.endorsed.dirs} System Property. Path of endorsed directory or directories. + *

+ *

+ * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + *

+ *

+ * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + *

+ * + * @since Java 1.4 + */ + public static final String JAVA_ENDORSED_DIRS = getSystemProperty("java.endorsed.dirs"); + + /** + *

+ * The {@code java.ext.dirs} System Property. Path of extension directory or directories. + *

+ *

+ * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + *

+ *

+ * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + *

+ * + * @since Java 1.3 + */ + public static final String JAVA_EXT_DIRS = getSystemProperty("java.ext.dirs"); + + /** + *

+ * The {@code java.home} System Property. Java installation directory. + *

+ *

+ * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + *

+ *

+ * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + *

+ * + * @since Java 1.1 + */ + public static final String JAVA_HOME = getSystemProperty(JAVA_HOME_KEY); + + /** + *

+ * The {@code java.io.tmpdir} System Property. Default temp file path. + *

+ *

+ * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + *

+ *

+ * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + *

+ * + * @since Java 1.2 + */ + public static final String JAVA_IO_TMPDIR = getSystemProperty(JAVA_IO_TMPDIR_KEY); + + /** + *

+ * The {@code java.library.path} System Property. List of paths to search when loading libraries. + *

+ *

+ * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + *

+ *

+ * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + *

+ * + * @since Java 1.2 + */ + public static final String JAVA_LIBRARY_PATH = getSystemProperty("java.library.path"); + + /** + *

+ * The {@code java.runtime.name} System Property. Java Runtime Environment name. + *

+ *

+ * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + *

+ *

+ * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + *

+ * + * @since 2.0 + * @since Java 1.3 + */ + public static final String JAVA_RUNTIME_NAME = getSystemProperty("java.runtime.name"); + + /** + *

+ * The {@code java.runtime.version} System Property. Java Runtime Environment version. + *

+ *

+ * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + *

+ *

+ * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + *

+ * + * @since 2.0 + * @since Java 1.3 + */ + public static final String JAVA_RUNTIME_VERSION = getSystemProperty("java.runtime.version"); + + /** + *

+ * The {@code java.specification.name} System Property. Java Runtime Environment specification name. + *

+ *

+ * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + *

+ *

+ * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + *

+ * + * @since Java 1.2 + */ + public static final String JAVA_SPECIFICATION_NAME = getSystemProperty("java.specification.name"); + + /** + *

+ * The {@code java.specification.vendor} System Property. Java Runtime Environment specification vendor. + *

+ *

+ * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + *

+ *

+ * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + *

+ * + * @since Java 1.2 + */ + public static final String JAVA_SPECIFICATION_VENDOR = getSystemProperty("java.specification.vendor"); + + /** + *

+ * The {@code java.specification.version} System Property. Java Runtime Environment specification version. + *

+ *

+ * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + *

+ *

+ * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + *

+ * + * @since Java 1.3 + */ + public static final String JAVA_SPECIFICATION_VERSION = getSystemProperty("java.specification.version"); + private static final JavaVersion JAVA_SPECIFICATION_VERSION_AS_ENUM = JavaVersion.get(JAVA_SPECIFICATION_VERSION); + + /** + *

+ * The {@code java.util.prefs.PreferencesFactory} System Property. A class name. + *

+ *

+ * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + *

+ *

+ * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + *

+ * + * @since 2.1 + * @since Java 1.4 + */ + public static final String JAVA_UTIL_PREFS_PREFERENCES_FACTORY = + getSystemProperty("java.util.prefs.PreferencesFactory"); + + /** + *

+ * The {@code java.vendor} System Property. Java vendor-specific string. + *

+ *

+ * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + *

+ *

+ * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + *

+ * + * @since Java 1.1 + */ + public static final String JAVA_VENDOR = getSystemProperty("java.vendor"); + + /** + *

+ * The {@code java.vendor.url} System Property. Java vendor URL. + *

+ *

+ * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + *

+ *

+ * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + *

+ * + * @since Java 1.1 + */ + public static final String JAVA_VENDOR_URL = getSystemProperty("java.vendor.url"); + + /** + *

+ * The {@code java.version} System Property. Java version number. + *

+ *

+ * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + *

+ *

+ * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + *

+ * + * @since Java 1.1 + */ + public static final String JAVA_VERSION = getSystemProperty("java.version"); + + /** + *

+ * The {@code java.vm.info} System Property. Java Virtual Machine implementation info. + *

+ *

+ * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + *

+ *

+ * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + *

+ * + * @since 2.0 + * @since Java 1.2 + */ + public static final String JAVA_VM_INFO = getSystemProperty("java.vm.info"); + + /** + *

+ * The {@code java.vm.name} System Property. Java Virtual Machine implementation name. + *

+ *

+ * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + *

+ *

+ * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + *

+ * + * @since Java 1.2 + */ + public static final String JAVA_VM_NAME = getSystemProperty("java.vm.name"); + + /** + *

+ * The {@code java.vm.specification.name} System Property. Java Virtual Machine specification name. + *

+ *

+ * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + *

+ *

+ * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + *

+ * + * @since Java 1.2 + */ + public static final String JAVA_VM_SPECIFICATION_NAME = getSystemProperty("java.vm.specification.name"); + + /** + *

+ * The {@code java.vm.specification.vendor} System Property. Java Virtual Machine specification vendor. + *

+ *

+ * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + *

+ *

+ * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + *

+ * + * @since Java 1.2 + */ + public static final String JAVA_VM_SPECIFICATION_VENDOR = getSystemProperty("java.vm.specification.vendor"); + + /** + *

+ * The {@code java.vm.specification.version} System Property. Java Virtual Machine specification version. + *

+ *

+ * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + *

+ *

+ * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + *

+ * + * @since Java 1.2 + */ + public static final String JAVA_VM_SPECIFICATION_VERSION = getSystemProperty("java.vm.specification.version"); + + /** + *

+ * The {@code java.vm.vendor} System Property. Java Virtual Machine implementation vendor. + *

+ *

+ * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + *

+ *

+ * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + *

+ * + * @since Java 1.2 + */ + public static final String JAVA_VM_VENDOR = getSystemProperty("java.vm.vendor"); + + /** + *

+ * The {@code java.vm.version} System Property. Java Virtual Machine implementation version. + *

+ *

+ * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + *

+ *

+ * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + *

+ * + * @since Java 1.2 + */ + public static final String JAVA_VM_VERSION = getSystemProperty("java.vm.version"); + + /** + *

+ * The {@code line.separator} System Property. Line separator ("\n" on UNIX). + *

+ *

+ * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + *

+ *

+ * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + *

+ * + * @since Java 1.1 + */ + public static final String LINE_SEPARATOR = getSystemProperty("line.separator"); + + /** + *

+ * The {@code os.arch} System Property. Operating system architecture. + *

+ *

+ * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + *

+ *

+ * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + *

+ * + * @since Java 1.1 + */ + public static final String OS_ARCH = getSystemProperty("os.arch"); + + /** + *

+ * The {@code os.name} System Property. Operating system name. + *

+ *

+ * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + *

+ *

+ * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + *

+ * + * @since Java 1.1 + */ + public static final String OS_NAME = getSystemProperty("os.name"); + + /** + *

+ * The {@code os.version} System Property. Operating system version. + *

+ *

+ * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + *

+ *

+ * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + *

+ * + * @since Java 1.1 + */ + public static final String OS_VERSION = getSystemProperty("os.version"); + + /** + *

+ * The {@code path.separator} System Property. Path separator (":" on UNIX). + *

+ *

+ * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + *

+ *

+ * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + *

+ * + * @since Java 1.1 + */ + public static final String PATH_SEPARATOR = getSystemProperty("path.separator"); + + /** + *

+ * The {@code user.country} or {@code user.region} System Property. User's country code, such as {@code GB}. First + * in Java version 1.2 as {@code user.region}. Renamed to {@code user.country} in 1.4 + *

+ *

+ * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + *

+ *

+ * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + *

+ * + * @since 2.0 + * @since Java 1.2 + */ + public static final String USER_COUNTRY = getSystemProperty("user.country") == null ? + getSystemProperty("user.region") : getSystemProperty("user.country"); + + /** + *

+ * The {@code user.dir} System Property. User's current working directory. + *

+ *

+ * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + *

+ *

+ * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + *

+ * + * @since Java 1.1 + */ + public static final String USER_DIR = getSystemProperty(USER_DIR_KEY); + + /** + *

+ * The {@code user.home} System Property. User's home directory. + *

+ *

+ * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + *

+ *

+ * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + *

+ * + * @since Java 1.1 + */ + public static final String USER_HOME = getSystemProperty(USER_HOME_KEY); + + /** + *

+ * The {@code user.language} System Property. User's language code, such as {@code "en"}. + *

+ *

+ * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + *

+ *

+ * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + *

+ * + * @since 2.0 + * @since Java 1.2 + */ + public static final String USER_LANGUAGE = getSystemProperty("user.language"); + + /** + *

+ * The {@code user.name} System Property. User's account name. + *

+ *

+ * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + *

+ *

+ * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + *

+ * + * @since Java 1.1 + */ + public static final String USER_NAME = getSystemProperty("user.name"); + + /** + *

+ * The {@code user.timezone} System Property. For example: {@code "America/Los_Angeles"}. + *

+ *

+ * Defaults to {@code null} if the runtime does not have security access to read this property or the property does + * not exist. + *

+ *

+ * This value is initialized when the class is loaded. If {@link System#setProperty(String,String)} or + * {@link System#setProperties(java.util.Properties)} is called after this class is loaded, the value will be out of + * sync with that System property. + *

+ * + * @since 2.1 + */ + public static final String USER_TIMEZONE = getSystemProperty("user.timezone"); + + // Java version checks + // ----------------------------------------------------------------------- + // These MUST be declared after those above as they depend on the + // values being set up + + /** + *

+ * Is {@code true} if this is Java version 1.1 (also 1.1.x versions). + *

+ *

+ * The field will return {@code false} if {@link #JAVA_VERSION} is {@code null}. + *

+ */ + public static final boolean IS_JAVA_1_1 = getJavaVersionMatches("1.1"); + + /** + *

+ * Is {@code true} if this is Java version 1.2 (also 1.2.x versions). + *

+ *

+ * The field will return {@code false} if {@link #JAVA_VERSION} is {@code null}. + *

+ */ + public static final boolean IS_JAVA_1_2 = getJavaVersionMatches("1.2"); + + /** + *

+ * Is {@code true} if this is Java version 1.3 (also 1.3.x versions). + *

+ *

+ * The field will return {@code false} if {@link #JAVA_VERSION} is {@code null}. + *

+ */ + public static final boolean IS_JAVA_1_3 = getJavaVersionMatches("1.3"); + + /** + *

+ * Is {@code true} if this is Java version 1.4 (also 1.4.x versions). + *

+ *

+ * The field will return {@code false} if {@link #JAVA_VERSION} is {@code null}. + *

+ */ + public static final boolean IS_JAVA_1_4 = getJavaVersionMatches("1.4"); + + /** + *

+ * Is {@code true} if this is Java version 1.5 (also 1.5.x versions). + *

+ *

+ * The field will return {@code false} if {@link #JAVA_VERSION} is {@code null}. + *

+ */ + public static final boolean IS_JAVA_1_5 = getJavaVersionMatches("1.5"); + + /** + *

+ * Is {@code true} if this is Java version 1.6 (also 1.6.x versions). + *

+ *

+ * The field will return {@code false} if {@link #JAVA_VERSION} is {@code null}. + *

+ */ + public static final boolean IS_JAVA_1_6 = getJavaVersionMatches("1.6"); + + /** + *

+ * Is {@code true} if this is Java version 1.7 (also 1.7.x versions). + *

+ *

+ * The field will return {@code false} if {@link #JAVA_VERSION} is {@code null}. + *

+ * + * @since 3.0 + */ + public static final boolean IS_JAVA_1_7 = getJavaVersionMatches("1.7"); + + // Operating system checks + // ----------------------------------------------------------------------- + // These MUST be declared after those above as they depend on the + // values being set up + // OS names from http://www.vamphq.com/os.html + // Selected ones included - please advise dev@commons.apache.org + // if you want another added or a mistake corrected + + /** + *

+ * Is {@code true} if this is AIX. + *

+ *

+ * The field will return {@code false} if {@code OS_NAME} is {@code null}. + *

+ * + * @since 2.0 + */ + public static final boolean IS_OS_AIX = getOSMatchesName("AIX"); + + /** + *

+ * Is {@code true} if this is HP-UX. + *

+ *

+ * The field will return {@code false} if {@code OS_NAME} is {@code null}. + *

+ * + * @since 2.0 + */ + public static final boolean IS_OS_HP_UX = getOSMatchesName("HP-UX"); + + /** + *

+ * Is {@code true} if this is Irix. + *

+ *

+ * The field will return {@code false} if {@code OS_NAME} is {@code null}. + *

+ * + * @since 2.0 + */ + public static final boolean IS_OS_IRIX = getOSMatchesName("Irix"); + + /** + *

+ * Is {@code true} if this is Linux. + *

+ *

+ * The field will return {@code false} if {@code OS_NAME} is {@code null}. + *

+ * + * @since 2.0 + */ + public static final boolean IS_OS_LINUX = getOSMatchesName("Linux") || getOSMatchesName("LINUX"); + + /** + *

+ * Is {@code true} if this is Mac. + *

+ *

+ * The field will return {@code false} if {@code OS_NAME} is {@code null}. + *

+ * + * @since 2.0 + */ + public static final boolean IS_OS_MAC = getOSMatchesName("Mac"); + + /** + *

+ * Is {@code true} if this is Mac. + *

+ *

+ * The field will return {@code false} if {@code OS_NAME} is {@code null}. + *

+ * + * @since 2.0 + */ + public static final boolean IS_OS_MAC_OSX = getOSMatchesName("Mac OS X"); + + /** + *

+ * Is {@code true} if this is FreeBSD. + *

+ *

+ * The field will return {@code false} if {@code OS_NAME} is {@code null}. + *

+ * + * @since 3.1 + */ + public static final boolean IS_OS_FREE_BSD = getOSMatchesName("FreeBSD"); + + /** + *

+ * Is {@code true} if this is OpenBSD. + *

+ *

+ * The field will return {@code false} if {@code OS_NAME} is {@code null}. + *

+ * + * @since 3.1 + */ + public static final boolean IS_OS_OPEN_BSD = getOSMatchesName("OpenBSD"); + + /** + *

+ * Is {@code true} if this is NetBSD. + *

+ *

+ * The field will return {@code false} if {@code OS_NAME} is {@code null}. + *

+ * + * @since 3.1 + */ + public static final boolean IS_OS_NET_BSD = getOSMatchesName("NetBSD"); + + /** + *

+ * Is {@code true} if this is OS/2. + *

+ *

+ * The field will return {@code false} if {@code OS_NAME} is {@code null}. + *

+ * + * @since 2.0 + */ + public static final boolean IS_OS_OS2 = getOSMatchesName("OS/2"); + + /** + *

+ * Is {@code true} if this is Solaris. + *

+ *

+ * The field will return {@code false} if {@code OS_NAME} is {@code null}. + *

+ * + * @since 2.0 + */ + public static final boolean IS_OS_SOLARIS = getOSMatchesName("Solaris"); + + /** + *

+ * Is {@code true} if this is SunOS. + *

+ *

+ * The field will return {@code false} if {@code OS_NAME} is {@code null}. + *

+ * + * @since 2.0 + */ + public static final boolean IS_OS_SUN_OS = getOSMatchesName("SunOS"); + + /** + *

+ * Is {@code true} if this is a UNIX like system, as in any of AIX, HP-UX, Irix, Linux, MacOSX, Solaris or SUN OS. + *

+ *

+ * The field will return {@code false} if {@code OS_NAME} is {@code null}. + *

+ * + * @since 2.1 + */ + public static final boolean IS_OS_UNIX = IS_OS_AIX || IS_OS_HP_UX || IS_OS_IRIX || IS_OS_LINUX || IS_OS_MAC_OSX + || IS_OS_SOLARIS || IS_OS_SUN_OS || IS_OS_FREE_BSD || IS_OS_OPEN_BSD || IS_OS_NET_BSD; + + /** + *

+ * Is {@code true} if this is Windows. + *

+ *

+ * The field will return {@code false} if {@code OS_NAME} is {@code null}. + *

+ * + * @since 2.0 + */ + public static final boolean IS_OS_WINDOWS = getOSMatchesName(OS_NAME_WINDOWS_PREFIX); + + /** + *

+ * Is {@code true} if this is Windows 2000. + *

+ *

+ * The field will return {@code false} if {@code OS_NAME} is {@code null}. + *

+ * + * @since 2.0 + */ + public static final boolean IS_OS_WINDOWS_2000 = getOSMatches(OS_NAME_WINDOWS_PREFIX, "5.0"); + + /** + *

+ * Is {@code true} if this is Windows 2003. + *

+ *

+ * The field will return {@code false} if {@code OS_NAME} is {@code null}. + *

+ * + * @since 3.1 + */ + public static final boolean IS_OS_WINDOWS_2003 = getOSMatches(OS_NAME_WINDOWS_PREFIX, "5.2"); + + /** + *

+ * Is {@code true} if this is Windows 2008. + *

+ *

+ * The field will return {@code false} if {@code OS_NAME} is {@code null}. + *

+ * + * @since 3.1 + */ + public static final boolean IS_OS_WINDOWS_2008 = getOSMatches(OS_NAME_WINDOWS_PREFIX + " Server 2008", "6.1"); + + /** + *

+ * Is {@code true} if this is Windows 95. + *

+ *

+ * The field will return {@code false} if {@code OS_NAME} is {@code null}. + *

+ * + * @since 2.0 + */ + public static final boolean IS_OS_WINDOWS_95 = getOSMatches(OS_NAME_WINDOWS_PREFIX + " 9", "4.0"); + // Java 1.2 running on Windows98 returns 'Windows 95', hence the above + + /** + *

+ * Is {@code true} if this is Windows 98. + *

+ *

+ * The field will return {@code false} if {@code OS_NAME} is {@code null}. + *

+ * + * @since 2.0 + */ + public static final boolean IS_OS_WINDOWS_98 = getOSMatches(OS_NAME_WINDOWS_PREFIX + " 9", "4.1"); + // Java 1.2 running on Windows98 returns 'Windows 95', hence the above + + /** + *

+ * Is {@code true} if this is Windows ME. + *

+ *

+ * The field will return {@code false} if {@code OS_NAME} is {@code null}. + *

+ * + * @since 2.0 + */ + public static final boolean IS_OS_WINDOWS_ME = getOSMatches(OS_NAME_WINDOWS_PREFIX, "4.9"); + // Java 1.2 running on WindowsME may return 'Windows 95', hence the above + + /** + *

+ * Is {@code true} if this is Windows NT. + *

+ *

+ * The field will return {@code false} if {@code OS_NAME} is {@code null}. + *

+ * + * @since 2.0 + */ + public static final boolean IS_OS_WINDOWS_NT = getOSMatchesName(OS_NAME_WINDOWS_PREFIX + " NT"); + // Windows 2000 returns 'Windows 2000' but may suffer from same Java1.2 problem + + /** + *

+ * Is {@code true} if this is Windows XP. + *

+ *

+ * The field will return {@code false} if {@code OS_NAME} is {@code null}. + *

+ * + * @since 2.0 + */ + public static final boolean IS_OS_WINDOWS_XP = getOSMatches(OS_NAME_WINDOWS_PREFIX, "5.1"); + + // ----------------------------------------------------------------------- + /** + *

+ * Is {@code true} if this is Windows Vista. + *

+ *

+ * The field will return {@code false} if {@code OS_NAME} is {@code null}. + *

+ * + * @since 2.4 + */ + public static final boolean IS_OS_WINDOWS_VISTA = getOSMatches(OS_NAME_WINDOWS_PREFIX, "6.0"); + + /** + *

+ * Is {@code true} if this is Windows 7. + *

+ *

+ * The field will return {@code false} if {@code OS_NAME} is {@code null}. + *

+ * + * @since 3.0 + */ + public static final boolean IS_OS_WINDOWS_7 = getOSMatches(OS_NAME_WINDOWS_PREFIX, "6.1"); + + /** + *

+ * Gets the Java home directory as a {@code File}. + *

+ * + * @return a directory + * @throws SecurityException if a security manager exists and its {@code checkPropertyAccess} method doesn't allow + * access to the specified system property. + * @see System#getProperty(String) + * @since 2.1 + */ + public static File getJavaHome() { + return new File(System.getProperty(JAVA_HOME_KEY)); + } + + /** + *

+ * Gets the Java IO temporary directory as a {@code File}. + *

+ * + * @return a directory + * @throws SecurityException if a security manager exists and its {@code checkPropertyAccess} method doesn't allow + * access to the specified system property. + * @see System#getProperty(String) + * @since 2.1 + */ + public static File getJavaIoTmpDir() { + return new File(System.getProperty(JAVA_IO_TMPDIR_KEY)); + } + + /** + *

+ * Decides if the Java version matches. + *

+ * + * @param versionPrefix the prefix for the java version + * @return true if matches, or false if not or can't determine + */ + private static boolean getJavaVersionMatches(String versionPrefix) { + return isJavaVersionMatch(JAVA_SPECIFICATION_VERSION, versionPrefix); + } + + /** + * Decides if the operating system matches. + * + * @param osNamePrefix the prefix for the os name + * @param osVersionPrefix the prefix for the version + * @return true if matches, or false if not or can't determine + */ + private static boolean getOSMatches(String osNamePrefix, String osVersionPrefix) { + return isOSMatch(OS_NAME, OS_VERSION, osNamePrefix, osVersionPrefix); + } + + /** + * Decides if the operating system matches. + * + * @param osNamePrefix the prefix for the os name + * @return true if matches, or false if not or can't determine + */ + private static boolean getOSMatchesName(String osNamePrefix) { + return isOSNameMatch(OS_NAME, osNamePrefix); + } + + // ----------------------------------------------------------------------- + /** + *

+ * Gets a System property, defaulting to {@code null} if the property cannot be read. + *

+ *

+ * If a {@code SecurityException} is caught, the return value is {@code null} and a message is written to + * {@code System.err}. + *

+ * + * @param property the system property name + * @return the system property value or {@code null} if a security problem occurs + */ + private static String getSystemProperty(String property) { + try { + return System.getProperty(property); + } catch (SecurityException ex) { + // we are not allowed to look at this property + System.err.println("Caught a SecurityException reading the system property '" + property + + "'; the SystemUtils property value will default to null."); + return null; + } + } + + /** + *

+ * Gets the user directory as a {@code File}. + *

+ * + * @return a directory + * @throws SecurityException if a security manager exists and its {@code checkPropertyAccess} method doesn't allow + * access to the specified system property. + * @see System#getProperty(String) + * @since 2.1 + */ + public static File getUserDir() { + return new File(System.getProperty(USER_DIR_KEY)); + } + + /** + *

+ * Gets the user home directory as a {@code File}. + *

+ * + * @return a directory + * @throws SecurityException if a security manager exists and its {@code checkPropertyAccess} method doesn't allow + * access to the specified system property. + * @see System#getProperty(String) + * @since 2.1 + */ + public static File getUserHome() { + return new File(System.getProperty(USER_HOME_KEY)); + } + + /** + * Returns whether the {@link #JAVA_AWT_HEADLESS} value is {@code true}. + * + * @return {@code true} if {@code JAVA_AWT_HEADLESS} is {@code "true"}, {@code false} otherwise. + * @see #JAVA_AWT_HEADLESS + * @since 2.1 + * @since Java 1.4 + */ + public static boolean isJavaAwtHeadless() { + return JAVA_AWT_HEADLESS != null ? JAVA_AWT_HEADLESS.equals(Boolean.TRUE.toString()) : false; + } + + /** + *

+ * Is the Java version at least the requested version. + *

+ *

+ * Example input: + *

+ *
    + *
  • {@code 1.2f} to test for Java 1.2
  • + *
  • {@code 1.31f} to test for Java 1.3.1
  • + *
+ * + * @param requiredVersion the required version, for example 1.31f + * @return {@code true} if the actual version is equal or greater than the required version + */ + public static boolean isJavaVersionAtLeast(JavaVersion requiredVersion) { + return JAVA_SPECIFICATION_VERSION_AS_ENUM.atLeast(requiredVersion); + } + + /** + *

+ * Decides if the Java version matches. + *

+ *

+ * This method is package private instead of private to support unit test invocation. + *

+ * + * @param version the actual Java version + * @param versionPrefix the prefix for the expected Java version + * @return true if matches, or false if not or can't determine + */ + static boolean isJavaVersionMatch(String version, String versionPrefix) { + if (version == null) { + return false; + } + return version.startsWith(versionPrefix); + } + + /** + * Decides if the operating system matches. + *

+ * This method is package private instead of private to support unit test invocation. + *

+ * + * @param osName the actual OS name + * @param osVersion the actual OS version + * @param osNamePrefix the prefix for the expected OS name + * @param osVersionPrefix the prefix for the expected OS version + * @return true if matches, or false if not or can't determine + */ + static boolean isOSMatch(String osName, String osVersion, String osNamePrefix, String osVersionPrefix) { + if (osName == null || osVersion == null) { + return false; + } + return osName.startsWith(osNamePrefix) && osVersion.startsWith(osVersionPrefix); + } + + /** + * Decides if the operating system matches. + *

+ * This method is package private instead of private to support unit test invocation. + *

+ * + * @param osName the actual OS name + * @param osNamePrefix the prefix for the expected OS name + * @return true if matches, or false if not or can't determine + */ + static boolean isOSNameMatch(String osName, String osNamePrefix) { + if (osName == null) { + return false; + } + return osName.startsWith(osNamePrefix); + } + + // ----------------------------------------------------------------------- + /** + *

+ * SystemUtils instances should NOT be constructed in standard programming. Instead, the class should be used as + * {@code SystemUtils.FILE_SEPARATOR}. + *

+ *

+ * This constructor is public to permit tools that require a JavaBean instance to operate. + *

+ */ + public SystemUtils() { + super(); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/SystemUtilsTest.java b/ApacheCommonsLang/org/apache/commons/lang3/SystemUtilsTest.java new file mode 100644 index 0000000..1d4bfd5 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/SystemUtilsTest.java @@ -0,0 +1,364 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.commons.lang3; + +import static org.apache.commons.lang3.JavaVersion.JAVA_1_4; + +import java.io.File; +import java.lang.reflect.Constructor; +import java.lang.reflect.Modifier; +import java.util.Locale; + +import junit.framework.Assert; +import junit.framework.TestCase; + +/** + * Unit tests {@link org.apache.commons.lang3.SystemUtils}. + * + * Only limited testing can be performed. + * + * @version $Id: SystemUtilsTest.java 1144929 2011-07-10 18:26:16Z ggregory $ + */ +public class SystemUtilsTest extends TestCase { + + public SystemUtilsTest(String name) { + super(name); + } + + public void testConstructor() { + assertNotNull(new SystemUtils()); + Constructor[] cons = SystemUtils.class.getDeclaredConstructors(); + assertEquals(1, cons.length); + assertEquals(true, Modifier.isPublic(cons[0].getModifiers())); + assertEquals(true, Modifier.isPublic(SystemUtils.class.getModifiers())); + assertEquals(false, Modifier.isFinal(SystemUtils.class.getModifiers())); + } + + /** + * Assums no security manager exists. + */ + public void testGetJavaHome() { + File dir = SystemUtils.getJavaHome(); + Assert.assertNotNull(dir); + Assert.assertTrue(dir.exists()); + } + + /** + * Assums no security manager exists. + */ + public void testGetJavaIoTmpDir() { + File dir = SystemUtils.getJavaIoTmpDir(); + Assert.assertNotNull(dir); + Assert.assertTrue(dir.exists()); + } + + /** + * Assums no security manager exists. + */ + public void testGetUserDir() { + File dir = SystemUtils.getUserDir(); + Assert.assertNotNull(dir); + Assert.assertTrue(dir.exists()); + } + + /** + * Assums no security manager exists. + */ + public void testGetUserHome() { + File dir = SystemUtils.getUserHome(); + Assert.assertNotNull(dir); + Assert.assertTrue(dir.exists()); + } + + public void testIS_JAVA() { + String javaVersion = System.getProperty("java.version"); + if (javaVersion == null) { + assertEquals(false, SystemUtils.IS_JAVA_1_1); + assertEquals(false, SystemUtils.IS_JAVA_1_2); + assertEquals(false, SystemUtils.IS_JAVA_1_3); + assertEquals(false, SystemUtils.IS_JAVA_1_4); + assertEquals(false, SystemUtils.IS_JAVA_1_5); + assertEquals(false, SystemUtils.IS_JAVA_1_6); + assertEquals(false, SystemUtils.IS_JAVA_1_7); + } else if (javaVersion.startsWith("1.1")) { + assertEquals(true, SystemUtils.IS_JAVA_1_1); + assertEquals(false, SystemUtils.IS_JAVA_1_2); + assertEquals(false, SystemUtils.IS_JAVA_1_3); + assertEquals(false, SystemUtils.IS_JAVA_1_4); + assertEquals(false, SystemUtils.IS_JAVA_1_5); + assertEquals(false, SystemUtils.IS_JAVA_1_6); + assertEquals(false, SystemUtils.IS_JAVA_1_7); + } else if (javaVersion.startsWith("1.2")) { + assertEquals(false, SystemUtils.IS_JAVA_1_1); + assertEquals(true, SystemUtils.IS_JAVA_1_2); + assertEquals(false, SystemUtils.IS_JAVA_1_3); + assertEquals(false, SystemUtils.IS_JAVA_1_4); + assertEquals(false, SystemUtils.IS_JAVA_1_5); + assertEquals(false, SystemUtils.IS_JAVA_1_6); + assertEquals(false, SystemUtils.IS_JAVA_1_7); + } else if (javaVersion.startsWith("1.3")) { + assertEquals(false, SystemUtils.IS_JAVA_1_1); + assertEquals(false, SystemUtils.IS_JAVA_1_2); + assertEquals(true, SystemUtils.IS_JAVA_1_3); + assertEquals(false, SystemUtils.IS_JAVA_1_4); + assertEquals(false, SystemUtils.IS_JAVA_1_5); + assertEquals(false, SystemUtils.IS_JAVA_1_6); + assertEquals(false, SystemUtils.IS_JAVA_1_7); + } else if (javaVersion.startsWith("1.4")) { + assertEquals(false, SystemUtils.IS_JAVA_1_1); + assertEquals(false, SystemUtils.IS_JAVA_1_2); + assertEquals(false, SystemUtils.IS_JAVA_1_3); + assertEquals(true, SystemUtils.IS_JAVA_1_4); + assertEquals(false, SystemUtils.IS_JAVA_1_5); + assertEquals(false, SystemUtils.IS_JAVA_1_6); + assertEquals(false, SystemUtils.IS_JAVA_1_7); + } else if (javaVersion.startsWith("1.5")) { + assertEquals(false, SystemUtils.IS_JAVA_1_1); + assertEquals(false, SystemUtils.IS_JAVA_1_2); + assertEquals(false, SystemUtils.IS_JAVA_1_3); + assertEquals(false, SystemUtils.IS_JAVA_1_4); + assertEquals(true, SystemUtils.IS_JAVA_1_5); + assertEquals(false, SystemUtils.IS_JAVA_1_6); + assertEquals(false, SystemUtils.IS_JAVA_1_7); + } else if (javaVersion.startsWith("1.6")) { + assertEquals(false, SystemUtils.IS_JAVA_1_1); + assertEquals(false, SystemUtils.IS_JAVA_1_2); + assertEquals(false, SystemUtils.IS_JAVA_1_3); + assertEquals(false, SystemUtils.IS_JAVA_1_4); + assertEquals(false, SystemUtils.IS_JAVA_1_5); + assertEquals(true, SystemUtils.IS_JAVA_1_6); + assertEquals(false, SystemUtils.IS_JAVA_1_7); + } else { + System.out.println("Can't test IS_JAVA value"); + } + } + + public void testIS_OS() { + String osName = System.getProperty("os.name"); + if (osName == null) { + assertEquals(false, SystemUtils.IS_OS_WINDOWS); + assertEquals(false, SystemUtils.IS_OS_UNIX); + assertEquals(false, SystemUtils.IS_OS_SOLARIS); + assertEquals(false, SystemUtils.IS_OS_LINUX); + assertEquals(false, SystemUtils.IS_OS_MAC_OSX); + } else if (osName.startsWith("Windows")) { + assertEquals(false, SystemUtils.IS_OS_UNIX); + assertEquals(true, SystemUtils.IS_OS_WINDOWS); + } else if (osName.startsWith("Solaris")) { + assertEquals(true, SystemUtils.IS_OS_SOLARIS); + assertEquals(true, SystemUtils.IS_OS_UNIX); + assertEquals(false, SystemUtils.IS_OS_WINDOWS); + } else if (osName.toLowerCase(Locale.ENGLISH).startsWith("linux")) { + assertEquals(true, SystemUtils.IS_OS_LINUX); + assertEquals(true, SystemUtils.IS_OS_UNIX); + assertEquals(false, SystemUtils.IS_OS_WINDOWS); + } else if (osName.startsWith("Mac OS X")) { + assertEquals(true, SystemUtils.IS_OS_MAC_OSX); + assertEquals(true, SystemUtils.IS_OS_UNIX); + assertEquals(false, SystemUtils.IS_OS_WINDOWS); + } else if (osName.startsWith("OS/2")) { + assertEquals(true, SystemUtils.IS_OS_OS2); + assertEquals(false, SystemUtils.IS_OS_UNIX); + assertEquals(false, SystemUtils.IS_OS_WINDOWS); + } else if (osName.startsWith("SunOS")) { + assertEquals(true, SystemUtils.IS_OS_SUN_OS); + assertEquals(true, SystemUtils.IS_OS_UNIX); + assertEquals(false, SystemUtils.IS_OS_WINDOWS); + } else { + System.out.println("Can't test IS_OS value"); + } + } + + public void testJavaVersionMatches() { + String javaVersion = null; + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.0")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.1")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.2")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.3")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.4")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.5")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.6")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.7")); + javaVersion = ""; + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.0")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.1")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.2")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.3")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.4")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.5")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.6")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.7")); + javaVersion = "1.0"; + assertEquals(true, SystemUtils.isJavaVersionMatch(javaVersion, "1.0")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.1")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.2")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.3")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.4")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.5")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.6")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.7")); + javaVersion = "1.1"; + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.0")); + assertEquals(true, SystemUtils.isJavaVersionMatch(javaVersion, "1.1")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.2")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.3")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.4")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.5")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.6")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.7")); + javaVersion = "1.2"; + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.0")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.1")); + assertEquals(true, SystemUtils.isJavaVersionMatch(javaVersion, "1.2")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.3")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.4")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.5")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.6")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.7")); + javaVersion = "1.3.0"; + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.0")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.1")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.2")); + assertEquals(true, SystemUtils.isJavaVersionMatch(javaVersion, "1.3")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.4")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.5")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.6")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.7")); + javaVersion = "1.3.1"; + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.0")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.1")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.2")); + assertEquals(true, SystemUtils.isJavaVersionMatch(javaVersion, "1.3")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.4")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.5")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.6")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.7")); + javaVersion = "1.4.0"; + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.0")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.1")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.2")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.3")); + assertEquals(true, SystemUtils.isJavaVersionMatch(javaVersion, "1.4")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.5")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.6")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.7")); + javaVersion = "1.4.1"; + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.0")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.1")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.2")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.3")); + assertEquals(true, SystemUtils.isJavaVersionMatch(javaVersion, "1.4")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.5")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.6")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.7")); + javaVersion = "1.4.2"; + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.0")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.1")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.2")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.3")); + assertEquals(true, SystemUtils.isJavaVersionMatch(javaVersion, "1.4")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.5")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.6")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.7")); + javaVersion = "1.5.0"; + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.0")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.1")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.2")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.3")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.4")); + assertEquals(true, SystemUtils.isJavaVersionMatch(javaVersion, "1.5")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.6")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.7")); + javaVersion = "1.6.0"; + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.0")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.1")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.2")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.3")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.4")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.5")); + assertEquals(true, SystemUtils.isJavaVersionMatch(javaVersion, "1.6")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.7")); + javaVersion = "1.7.0"; + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.0")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.1")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.2")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.3")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.4")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.5")); + assertEquals(false, SystemUtils.isJavaVersionMatch(javaVersion, "1.6")); + assertEquals(true, SystemUtils.isJavaVersionMatch(javaVersion, "1.7")); + } + + public void testOSMatchesName() { + String osName = null; + assertEquals(false, SystemUtils.isOSNameMatch(osName, "Windows")); + osName = ""; + assertEquals(false, SystemUtils.isOSNameMatch(osName, "Windows")); + osName = "Windows 95"; + assertEquals(true, SystemUtils.isOSNameMatch(osName, "Windows")); + osName = "Windows NT"; + assertEquals(true, SystemUtils.isOSNameMatch(osName, "Windows")); + osName = "OS/2"; + assertEquals(false, SystemUtils.isOSNameMatch(osName, "Windows")); + } + + public void testOSMatchesNameAndVersion() { + String osName = null; + String osVersion = null; + assertEquals(false, SystemUtils.isOSMatch(osName, osVersion, "Windows 9", "4.1")); + osName = ""; + osVersion = ""; + assertEquals(false, SystemUtils.isOSMatch(osName, osVersion, "Windows 9", "4.1")); + osName = "Windows 95"; + osVersion = "4.0"; + assertEquals(false, SystemUtils.isOSMatch(osName, osVersion, "Windows 9", "4.1")); + osName = "Windows 95"; + osVersion = "4.1"; + assertEquals(true, SystemUtils.isOSMatch(osName, osVersion, "Windows 9", "4.1")); + osName = "Windows 98"; + osVersion = "4.1"; + assertEquals(true, SystemUtils.isOSMatch(osName, osVersion, "Windows 9", "4.1")); + osName = "Windows NT"; + osVersion = "4.0"; + assertEquals(false, SystemUtils.isOSMatch(osName, osVersion, "Windows 9", "4.1")); + osName = "OS/2"; + osVersion = "4.0"; + assertEquals(false, SystemUtils.isOSMatch(osName, osVersion, "Windows 9", "4.1")); + } + + public void testJavaAwtHeadless() { + boolean atLeastJava14 = SystemUtils.isJavaVersionAtLeast(JAVA_1_4); + String expectedStringValue = System.getProperty("java.awt.headless"); + String expectedStringValueWithDefault = System.getProperty("java.awt.headless", "false"); + assertNotNull(expectedStringValueWithDefault); + if (atLeastJava14) { + boolean expectedValue = Boolean.valueOf(expectedStringValue).booleanValue(); + if (expectedStringValue != null) { + assertEquals(expectedStringValue, SystemUtils.JAVA_AWT_HEADLESS); + } + assertEquals(expectedValue, SystemUtils.isJavaAwtHeadless()); + } else { + assertNull(expectedStringValue); + assertNull(SystemUtils.JAVA_AWT_HEADLESS); + assertEquals(expectedStringValueWithDefault, "" + SystemUtils.isJavaAwtHeadless()); + } + assertEquals(expectedStringValueWithDefault, "" + SystemUtils.isJavaAwtHeadless()); + } +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/Validate.java b/ApacheCommonsLang/org/apache/commons/lang3/Validate.java new file mode 100644 index 0000000..d0d72d8 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/Validate.java @@ -0,0 +1,1080 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.regex.Pattern; + +/** + *

This class assists in validating arguments. The validation methods are + * based along the following principles: + *

    + *
  • An invalid {@code null} argument causes a {@link NullPointerException}.
  • + *
  • A non-{@code null} argument causes an {@link IllegalArgumentException}.
  • + *
  • An invalid index into an array/collection/map/string causes an {@link IndexOutOfBoundsException}.
  • + *
+ * + *

All exceptions messages are + * format strings + * as defined by the Java platform. For example:

+ * + *
+ * Validate.isTrue(i > 0, "The value must be greater than zero: %d", i);
+ * Validate.notNull(surname, "The surname must not be %s", null);
+ * 
+ * + *

#ThreadSafe#

+ * @version $Id: Validate.java 1301150 2012-03-15 19:00:56Z mbenson $ + * @see java.lang.String#format(String, Object...) + * @since 2.0 + */ +public class Validate { + + private static final String DEFAULT_EXCLUSIVE_BETWEEN_EX_MESSAGE = + "The value %s is not in the specified exclusive range of %s to %s"; + private static final String DEFAULT_INCLUSIVE_BETWEEN_EX_MESSAGE = + "The value %s is not in the specified inclusive range of %s to %s"; + private static final String DEFAULT_MATCHES_PATTERN_EX = "The string %s does not match the pattern %s"; + private static final String DEFAULT_IS_NULL_EX_MESSAGE = "The validated object is null"; + private static final String DEFAULT_IS_TRUE_EX_MESSAGE = "The validated expression is false"; + private static final String DEFAULT_NO_NULL_ELEMENTS_ARRAY_EX_MESSAGE = + "The validated array contains null element at index: %d"; + private static final String DEFAULT_NO_NULL_ELEMENTS_COLLECTION_EX_MESSAGE = + "The validated collection contains null element at index: %d"; + private static final String DEFAULT_NOT_BLANK_EX_MESSAGE = "The validated character sequence is blank"; + private static final String DEFAULT_NOT_EMPTY_ARRAY_EX_MESSAGE = "The validated array is empty"; + private static final String DEFAULT_NOT_EMPTY_CHAR_SEQUENCE_EX_MESSAGE = + "The validated character sequence is empty"; + private static final String DEFAULT_NOT_EMPTY_COLLECTION_EX_MESSAGE = "The validated collection is empty"; + private static final String DEFAULT_NOT_EMPTY_MAP_EX_MESSAGE = "The validated map is empty"; + private static final String DEFAULT_VALID_INDEX_ARRAY_EX_MESSAGE = "The validated array index is invalid: %d"; + private static final String DEFAULT_VALID_INDEX_CHAR_SEQUENCE_EX_MESSAGE = + "The validated character sequence index is invalid: %d"; + private static final String DEFAULT_VALID_INDEX_COLLECTION_EX_MESSAGE = + "The validated collection index is invalid: %d"; + private static final String DEFAULT_VALID_STATE_EX_MESSAGE = "The validated state is false"; + private static final String DEFAULT_IS_ASSIGNABLE_EX_MESSAGE = "Cannot assign a %s to a %s"; + private static final String DEFAULT_IS_INSTANCE_OF_EX_MESSAGE = "Expected type: %s, actual: %s"; + + /** + * Constructor. This class should not normally be instantiated. + */ + public Validate() { + super(); + } + + // isTrue + //--------------------------------------------------------------------------------- + + /** + *

Validate that the argument condition is {@code true}; otherwise + * throwing an exception with the specified message. This method is useful when + * validating according to an arbitrary boolean expression, such as validating a + * primitive number or using your own custom validation expression.

+ * + *
Validate.isTrue(i > 0.0, "The value must be greater than zero: %d", i);
+ * + *

For performance reasons, the long value is passed as a separate parameter and + * appended to the exception message only in the case of an error.

+ * + * @param expression the boolean expression to check + * @param message the {@link String#format(String, Object...)} exception message if invalid, not null + * @param value the value to append to the message when invalid + * @throws IllegalArgumentException if expression is {@code false} + * @see #isTrue(boolean) + * @see #isTrue(boolean, String, double) + * @see #isTrue(boolean, String, Object...) + */ + public static void isTrue(boolean expression, String message, long value) { + if (expression == false) { + throw new IllegalArgumentException(String.format(message, Long.valueOf(value))); + } + } + + /** + *

Validate that the argument condition is {@code true}; otherwise + * throwing an exception with the specified message. This method is useful when + * validating according to an arbitrary boolean expression, such as validating a + * primitive number or using your own custom validation expression.

+ * + *
Validate.isTrue(d > 0.0, "The value must be greater than zero: %s", d);
+ * + *

For performance reasons, the double value is passed as a separate parameter and + * appended to the exception message only in the case of an error.

+ * + * @param expression the boolean expression to check + * @param message the {@link String#format(String, Object...)} exception message if invalid, not null + * @param value the value to append to the message when invalid + * @throws IllegalArgumentException if expression is {@code false} + * @see #isTrue(boolean) + * @see #isTrue(boolean, String, long) + * @see #isTrue(boolean, String, Object...) + */ + public static void isTrue(boolean expression, String message, double value) { + if (expression == false) { + throw new IllegalArgumentException(String.format(message, Double.valueOf(value))); + } + } + + /** + *

Validate that the argument condition is {@code true}; otherwise + * throwing an exception with the specified message. This method is useful when + * validating according to an arbitrary boolean expression, such as validating a + * primitive number or using your own custom validation expression.

+ * + *
+     * Validate.isTrue(i >= min && i <= max, "The value must be between %d and %d", min, max);
+     * Validate.isTrue(myObject.isOk(), "The object is not okay");
+ * + * @param expression the boolean expression to check + * @param message the {@link String#format(String, Object...)} exception message if invalid, not null + * @param values the optional values for the formatted exception message, null array not recommended + * @throws IllegalArgumentException if expression is {@code false} + * @see #isTrue(boolean) + * @see #isTrue(boolean, String, long) + * @see #isTrue(boolean, String, double) + */ + public static void isTrue(boolean expression, String message, Object... values) { + if (expression == false) { + throw new IllegalArgumentException(String.format(message, values)); + } + } + + /** + *

Validate that the argument condition is {@code true}; otherwise + * throwing an exception. This method is useful when validating according + * to an arbitrary boolean expression, such as validating a + * primitive number or using your own custom validation expression.

+ * + *
+     * Validate.isTrue(i > 0);
+     * Validate.isTrue(myObject.isOk());
+ * + *

The message of the exception is "The validated expression is + * false".

+ * + * @param expression the boolean expression to check + * @throws IllegalArgumentException if expression is {@code false} + * @see #isTrue(boolean, String, long) + * @see #isTrue(boolean, String, double) + * @see #isTrue(boolean, String, Object...) + */ + public static void isTrue(boolean expression) { + if (expression == false) { + throw new IllegalArgumentException(DEFAULT_IS_TRUE_EX_MESSAGE); + } + } + + // notNull + //--------------------------------------------------------------------------------- + + /** + *

Validate that the specified argument is not {@code null}; + * otherwise throwing an exception. + * + *

Validate.notNull(myObject, "The object must not be null");
+ * + *

The message of the exception is "The validated object is + * null".

+ * + * @param the object type + * @param object the object to check + * @return the validated object (never {@code null} for method chaining) + * @throws NullPointerException if the object is {@code null} + * @see #notNull(Object, String, Object...) + */ + public static T notNull(T object) { + return notNull(object, DEFAULT_IS_NULL_EX_MESSAGE); + } + + /** + *

Validate that the specified argument is not {@code null}; + * otherwise throwing an exception with the specified message. + * + *

Validate.notNull(myObject, "The object must not be null");
+ * + * @param the object type + * @param object the object to check + * @param message the {@link String#format(String, Object...)} exception message if invalid, not null + * @param values the optional values for the formatted exception message + * @return the validated object (never {@code null} for method chaining) + * @throws NullPointerException if the object is {@code null} + * @see #notNull(Object) + */ + public static T notNull(T object, String message, Object... values) { + if (object == null) { + throw new NullPointerException(String.format(message, values)); + } + return object; + } + + // notEmpty array + //--------------------------------------------------------------------------------- + + /** + *

Validate that the specified argument array is neither {@code null} + * nor a length of zero (no elements); otherwise throwing an exception + * with the specified message. + * + *

Validate.notEmpty(myArray, "The array must not be empty");
+ * + * @param the array type + * @param array the array to check, validated not null by this method + * @param message the {@link String#format(String, Object...)} exception message if invalid, not null + * @param values the optional values for the formatted exception message, null array not recommended + * @return the validated array (never {@code null} method for chaining) + * @throws NullPointerException if the array is {@code null} + * @throws IllegalArgumentException if the array is empty + * @see #notEmpty(Object[]) + */ + public static T[] notEmpty(T[] array, String message, Object... values) { + if (array == null) { + throw new NullPointerException(String.format(message, values)); + } + if (array.length == 0) { + throw new IllegalArgumentException(String.format(message, values)); + } + return array; + } + + /** + *

Validate that the specified argument array is neither {@code null} + * nor a length of zero (no elements); otherwise throwing an exception. + * + *

Validate.notEmpty(myArray);
+ * + *

The message in the exception is "The validated array is + * empty". + * + * @param the array type + * @param array the array to check, validated not null by this method + * @return the validated array (never {@code null} method for chaining) + * @throws NullPointerException if the array is {@code null} + * @throws IllegalArgumentException if the array is empty + * @see #notEmpty(Object[], String, Object...) + */ + public static T[] notEmpty(T[] array) { + return notEmpty(array, DEFAULT_NOT_EMPTY_ARRAY_EX_MESSAGE); + } + + // notEmpty collection + //--------------------------------------------------------------------------------- + + /** + *

Validate that the specified argument collection is neither {@code null} + * nor a size of zero (no elements); otherwise throwing an exception + * with the specified message. + * + *

Validate.notEmpty(myCollection, "The collection must not be empty");
+ * + * @param the collection type + * @param collection the collection to check, validated not null by this method + * @param message the {@link String#format(String, Object...)} exception message if invalid, not null + * @param values the optional values for the formatted exception message, null array not recommended + * @return the validated collection (never {@code null} method for chaining) + * @throws NullPointerException if the collection is {@code null} + * @throws IllegalArgumentException if the collection is empty + * @see #notEmpty(Object[]) + */ + public static > T notEmpty(T collection, String message, Object... values) { + if (collection == null) { + throw new NullPointerException(String.format(message, values)); + } + if (collection.isEmpty()) { + throw new IllegalArgumentException(String.format(message, values)); + } + return collection; + } + + /** + *

Validate that the specified argument collection is neither {@code null} + * nor a size of zero (no elements); otherwise throwing an exception. + * + *

Validate.notEmpty(myCollection);
+ * + *

The message in the exception is "The validated collection is + * empty".

+ * + * @param the collection type + * @param collection the collection to check, validated not null by this method + * @return the validated collection (never {@code null} method for chaining) + * @throws NullPointerException if the collection is {@code null} + * @throws IllegalArgumentException if the collection is empty + * @see #notEmpty(Collection, String, Object...) + */ + public static > T notEmpty(T collection) { + return notEmpty(collection, DEFAULT_NOT_EMPTY_COLLECTION_EX_MESSAGE); + } + + // notEmpty map + //--------------------------------------------------------------------------------- + + /** + *

Validate that the specified argument map is neither {@code null} + * nor a size of zero (no elements); otherwise throwing an exception + * with the specified message. + * + *

Validate.notEmpty(myMap, "The map must not be empty");
+ * + * @param the map type + * @param map the map to check, validated not null by this method + * @param message the {@link String#format(String, Object...)} exception message if invalid, not null + * @param values the optional values for the formatted exception message, null array not recommended + * @return the validated map (never {@code null} method for chaining) + * @throws NullPointerException if the map is {@code null} + * @throws IllegalArgumentException if the map is empty + * @see #notEmpty(Object[]) + */ + public static > T notEmpty(T map, String message, Object... values) { + if (map == null) { + throw new NullPointerException(String.format(message, values)); + } + if (map.isEmpty()) { + throw new IllegalArgumentException(String.format(message, values)); + } + return map; + } + + /** + *

Validate that the specified argument map is neither {@code null} + * nor a size of zero (no elements); otherwise throwing an exception. + * + *

Validate.notEmpty(myMap);
+ * + *

The message in the exception is "The validated map is + * empty".

+ * + * @param the map type + * @param map the map to check, validated not null by this method + * @return the validated map (never {@code null} method for chaining) + * @throws NullPointerException if the map is {@code null} + * @throws IllegalArgumentException if the map is empty + * @see #notEmpty(Map, String, Object...) + */ + public static > T notEmpty(T map) { + return notEmpty(map, DEFAULT_NOT_EMPTY_MAP_EX_MESSAGE); + } + + // notEmpty string + //--------------------------------------------------------------------------------- + + /** + *

Validate that the specified argument character sequence is + * neither {@code null} nor a length of zero (no characters); + * otherwise throwing an exception with the specified message. + * + *

Validate.notEmpty(myString, "The string must not be empty");
+ * + * @param the character sequence type + * @param chars the character sequence to check, validated not null by this method + * @param message the {@link String#format(String, Object...)} exception message if invalid, not null + * @param values the optional values for the formatted exception message, null array not recommended + * @return the validated character sequence (never {@code null} method for chaining) + * @throws NullPointerException if the character sequence is {@code null} + * @throws IllegalArgumentException if the character sequence is empty + * @see #notEmpty(CharSequence) + */ + public static T notEmpty(T chars, String message, Object... values) { + if (chars == null) { + throw new NullPointerException(String.format(message, values)); + } + if (chars.length() == 0) { + throw new IllegalArgumentException(String.format(message, values)); + } + return chars; + } + + /** + *

Validate that the specified argument character sequence is + * neither {@code null} nor a length of zero (no characters); + * otherwise throwing an exception with the specified message. + * + *

Validate.notEmpty(myString);
+ * + *

The message in the exception is "The validated + * character sequence is empty".

+ * + * @param the character sequence type + * @param chars the character sequence to check, validated not null by this method + * @return the validated character sequence (never {@code null} method for chaining) + * @throws NullPointerException if the character sequence is {@code null} + * @throws IllegalArgumentException if the character sequence is empty + * @see #notEmpty(CharSequence, String, Object...) + */ + public static T notEmpty(T chars) { + return notEmpty(chars, DEFAULT_NOT_EMPTY_CHAR_SEQUENCE_EX_MESSAGE); + } + + // notBlank string + //--------------------------------------------------------------------------------- + + /** + *

Validate that the specified argument character sequence is + * neither {@code null}, a length of zero (no characters), empty + * nor whitespace; otherwise throwing an exception with the specified + * message. + * + *

Validate.notBlank(myString, "The string must not be blank");
+ * + * @param the character sequence type + * @param chars the character sequence to check, validated not null by this method + * @param message the {@link String#format(String, Object...)} exception message if invalid, not null + * @param values the optional values for the formatted exception message, null array not recommended + * @return the validated character sequence (never {@code null} method for chaining) + * @throws NullPointerException if the character sequence is {@code null} + * @throws IllegalArgumentException if the character sequence is blank + * @see #notBlank(CharSequence) + * + * @since 3.0 + */ + public static T notBlank(T chars, String message, Object... values) { + if (chars == null) { + throw new NullPointerException(String.format(message, values)); + } + if (StringUtils.isBlank(chars)) { + throw new IllegalArgumentException(String.format(message, values)); + } + return chars; + } + + /** + *

Validate that the specified argument character sequence is + * neither {@code null}, a length of zero (no characters), empty + * nor whitespace; otherwise throwing an exception. + * + *

Validate.notBlank(myString);
+ * + *

The message in the exception is "The validated character + * sequence is blank".

+ * + * @param the character sequence type + * @param chars the character sequence to check, validated not null by this method + * @return the validated character sequence (never {@code null} method for chaining) + * @throws NullPointerException if the character sequence is {@code null} + * @throws IllegalArgumentException if the character sequence is blank + * @see #notBlank(CharSequence, String, Object...) + * + * @since 3.0 + */ + public static T notBlank(T chars) { + return notBlank(chars, DEFAULT_NOT_BLANK_EX_MESSAGE); + } + + // noNullElements array + //--------------------------------------------------------------------------------- + + /** + *

Validate that the specified argument array is neither + * {@code null} nor contains any elements that are {@code null}; + * otherwise throwing an exception with the specified message. + * + *

Validate.noNullElements(myArray, "The array contain null at position %d");
+ * + *

If the array is {@code null}, then the message in the exception + * is "The validated object is null".

+ * + *

If the array has a {@code null} element, then the iteration + * index of the invalid element is appended to the {@code values} + * argument.

+ * + * @param the array type + * @param array the array to check, validated not null by this method + * @param message the {@link String#format(String, Object...)} exception message if invalid, not null + * @param values the optional values for the formatted exception message, null array not recommended + * @return the validated array (never {@code null} method for chaining) + * @throws NullPointerException if the array is {@code null} + * @throws IllegalArgumentException if an element is {@code null} + * @see #noNullElements(Object[]) + */ + public static T[] noNullElements(T[] array, String message, Object... values) { + Validate.notNull(array); + for (int i = 0; i < array.length; i++) { + if (array[i] == null) { + Object[] values2 = ArrayUtils.add(values, Integer.valueOf(i)); + throw new IllegalArgumentException(String.format(message, values2)); + } + } + return array; + } + + /** + *

Validate that the specified argument array is neither + * {@code null} nor contains any elements that are {@code null}; + * otherwise throwing an exception. + * + *

Validate.noNullElements(myArray);
+ * + *

If the array is {@code null}, then the message in the exception + * is "The validated object is null".

+ * + *

If the array has a {@code null} element, then the message in the + * exception is "The validated array contains null element at index: + * " followed by the index.

+ * + * @param the array type + * @param array the array to check, validated not null by this method + * @return the validated array (never {@code null} method for chaining) + * @throws NullPointerException if the array is {@code null} + * @throws IllegalArgumentException if an element is {@code null} + * @see #noNullElements(Object[], String, Object...) + */ + public static T[] noNullElements(T[] array) { + return noNullElements(array, DEFAULT_NO_NULL_ELEMENTS_ARRAY_EX_MESSAGE); + } + + // noNullElements iterable + //--------------------------------------------------------------------------------- + + /** + *

Validate that the specified argument iterable is neither + * {@code null} nor contains any elements that are {@code null}; + * otherwise throwing an exception with the specified message. + * + *

Validate.noNullElements(myCollection, "The collection contains null at position %d");
+ * + *

If the iterable is {@code null}, then the message in the exception + * is "The validated object is null".

+ * + *

If the iterable has a {@code null} element, then the iteration + * index of the invalid element is appended to the {@code values} + * argument.

+ * + * @param the iterable type + * @param iterable the iterable to check, validated not null by this method + * @param message the {@link String#format(String, Object...)} exception message if invalid, not null + * @param values the optional values for the formatted exception message, null array not recommended + * @return the validated iterable (never {@code null} method for chaining) + * @throws NullPointerException if the array is {@code null} + * @throws IllegalArgumentException if an element is {@code null} + * @see #noNullElements(Iterable) + */ + public static > T noNullElements(T iterable, String message, Object... values) { + Validate.notNull(iterable); + int i = 0; + for (Iterator it = iterable.iterator(); it.hasNext(); i++) { + if (it.next() == null) { + Object[] values2 = ArrayUtils.addAll(values, Integer.valueOf(i)); + throw new IllegalArgumentException(String.format(message, values2)); + } + } + return iterable; + } + + /** + *

Validate that the specified argument iterable is neither + * {@code null} nor contains any elements that are {@code null}; + * otherwise throwing an exception. + * + *

Validate.noNullElements(myCollection);
+ * + *

If the iterable is {@code null}, then the message in the exception + * is "The validated object is null".

+ * + *

If the array has a {@code null} element, then the message in the + * exception is "The validated iterable contains null element at index: + * " followed by the index.

+ * + * @param the iterable type + * @param iterable the iterable to check, validated not null by this method + * @return the validated iterable (never {@code null} method for chaining) + * @throws NullPointerException if the array is {@code null} + * @throws IllegalArgumentException if an element is {@code null} + * @see #noNullElements(Iterable, String, Object...) + */ + public static > T noNullElements(T iterable) { + return noNullElements(iterable, DEFAULT_NO_NULL_ELEMENTS_COLLECTION_EX_MESSAGE); + } + + // validIndex array + //--------------------------------------------------------------------------------- + + /** + *

Validates that the index is within the bounds of the argument + * array; otherwise throwing an exception with the specified message.

+ * + *
Validate.validIndex(myArray, 2, "The array index is invalid: ");
+ * + *

If the array is {@code null}, then the message of the exception + * is "The validated object is null".

+ * + * @param the array type + * @param array the array to check, validated not null by this method + * @param index the index to check + * @param message the {@link String#format(String, Object...)} exception message if invalid, not null + * @param values the optional values for the formatted exception message, null array not recommended + * @return the validated array (never {@code null} for method chaining) + * @throws NullPointerException if the array is {@code null} + * @throws IndexOutOfBoundsException if the index is invalid + * @see #validIndex(Object[], int) + * + * @since 3.0 + */ + public static T[] validIndex(T[] array, int index, String message, Object... values) { + Validate.notNull(array); + if (index < 0 || index >= array.length) { + throw new IndexOutOfBoundsException(String.format(message, values)); + } + return array; + } + + /** + *

Validates that the index is within the bounds of the argument + * array; otherwise throwing an exception.

+ * + *
Validate.validIndex(myArray, 2);
+ * + *

If the array is {@code null}, then the message of the exception + * is "The validated object is null".

+ * + *

If the index is invalid, then the message of the exception is + * "The validated array index is invalid: " followed by the + * index.

+ * + * @param the array type + * @param array the array to check, validated not null by this method + * @param index the index to check + * @return the validated array (never {@code null} for method chaining) + * @throws NullPointerException if the array is {@code null} + * @throws IndexOutOfBoundsException if the index is invalid + * @see #validIndex(Object[], int, String, Object...) + * + * @since 3.0 + */ + public static T[] validIndex(T[] array, int index) { + return validIndex(array, index, DEFAULT_VALID_INDEX_ARRAY_EX_MESSAGE, Integer.valueOf(index)); + } + + // validIndex collection + //--------------------------------------------------------------------------------- + + /** + *

Validates that the index is within the bounds of the argument + * collection; otherwise throwing an exception with the specified message.

+ * + *
Validate.validIndex(myCollection, 2, "The collection index is invalid: ");
+ * + *

If the collection is {@code null}, then the message of the + * exception is "The validated object is null".

+ * + * @param the collection type + * @param collection the collection to check, validated not null by this method + * @param index the index to check + * @param message the {@link String#format(String, Object...)} exception message if invalid, not null + * @param values the optional values for the formatted exception message, null array not recommended + * @return the validated collection (never {@code null} for chaining) + * @throws NullPointerException if the collection is {@code null} + * @throws IndexOutOfBoundsException if the index is invalid + * @see #validIndex(Collection, int) + * + * @since 3.0 + */ + public static > T validIndex(T collection, int index, String message, Object... values) { + Validate.notNull(collection); + if (index < 0 || index >= collection.size()) { + throw new IndexOutOfBoundsException(String.format(message, values)); + } + return collection; + } + + /** + *

Validates that the index is within the bounds of the argument + * collection; otherwise throwing an exception.

+ * + *
Validate.validIndex(myCollection, 2);
+ * + *

If the index is invalid, then the message of the exception + * is "The validated collection index is invalid: " + * followed by the index.

+ * + * @param the collection type + * @param collection the collection to check, validated not null by this method + * @param index the index to check + * @return the validated collection (never {@code null} for method chaining) + * @throws NullPointerException if the collection is {@code null} + * @throws IndexOutOfBoundsException if the index is invalid + * @see #validIndex(Collection, int, String, Object...) + * + * @since 3.0 + */ + public static > T validIndex(T collection, int index) { + return validIndex(collection, index, DEFAULT_VALID_INDEX_COLLECTION_EX_MESSAGE, Integer.valueOf(index)); + } + + // validIndex string + //--------------------------------------------------------------------------------- + + /** + *

Validates that the index is within the bounds of the argument + * character sequence; otherwise throwing an exception with the + * specified message.

+ * + *
Validate.validIndex(myStr, 2, "The string index is invalid: ");
+ * + *

If the character sequence is {@code null}, then the message + * of the exception is "The validated object is null".

+ * + * @param the character sequence type + * @param chars the character sequence to check, validated not null by this method + * @param index the index to check + * @param message the {@link String#format(String, Object...)} exception message if invalid, not null + * @param values the optional values for the formatted exception message, null array not recommended + * @return the validated character sequence (never {@code null} for method chaining) + * @throws NullPointerException if the character sequence is {@code null} + * @throws IndexOutOfBoundsException if the index is invalid + * @see #validIndex(CharSequence, int) + * + * @since 3.0 + */ + public static T validIndex(T chars, int index, String message, Object... values) { + Validate.notNull(chars); + if (index < 0 || index >= chars.length()) { + throw new IndexOutOfBoundsException(String.format(message, values)); + } + return chars; + } + + /** + *

Validates that the index is within the bounds of the argument + * character sequence; otherwise throwing an exception.

+ * + *
Validate.validIndex(myStr, 2);
+ * + *

If the character sequence is {@code null}, then the message + * of the exception is "The validated object is + * null".

+ * + *

If the index is invalid, then the message of the exception + * is "The validated character sequence index is invalid: " + * followed by the index.

+ * + * @param the character sequence type + * @param chars the character sequence to check, validated not null by this method + * @param index the index to check + * @return the validated character sequence (never {@code null} for method chaining) + * @throws NullPointerException if the character sequence is {@code null} + * @throws IndexOutOfBoundsException if the index is invalid + * @see #validIndex(CharSequence, int, String, Object...) + * + * @since 3.0 + */ + public static T validIndex(T chars, int index) { + return validIndex(chars, index, DEFAULT_VALID_INDEX_CHAR_SEQUENCE_EX_MESSAGE, Integer.valueOf(index)); + } + + // validState + //--------------------------------------------------------------------------------- + + /** + *

Validate that the stateful condition is {@code true}; otherwise + * throwing an exception. This method is useful when validating according + * to an arbitrary boolean expression, such as validating a + * primitive number or using your own custom validation expression.

+ * + *
+     * Validate.validState(field > 0);
+     * Validate.validState(this.isOk());
+ * + *

The message of the exception is "The validated state is + * false".

+ * + * @param expression the boolean expression to check + * @throws IllegalStateException if expression is {@code false} + * @see #validState(boolean, String, Object...) + * + * @since 3.0 + */ + public static void validState(boolean expression) { + if (expression == false) { + throw new IllegalStateException(DEFAULT_VALID_STATE_EX_MESSAGE); + } + } + + /** + *

Validate that the stateful condition is {@code true}; otherwise + * throwing an exception with the specified message. This method is useful when + * validating according to an arbitrary boolean expression, such as validating a + * primitive number or using your own custom validation expression.

+ * + *
Validate.validState(this.isOk(), "The state is not OK: %s", myObject);
+ * + * @param expression the boolean expression to check + * @param message the {@link String#format(String, Object...)} exception message if invalid, not null + * @param values the optional values for the formatted exception message, null array not recommended + * @throws IllegalStateException if expression is {@code false} + * @see #validState(boolean) + * + * @since 3.0 + */ + public static void validState(boolean expression, String message, Object... values) { + if (expression == false) { + throw new IllegalStateException(String.format(message, values)); + } + } + + // matchesPattern + //--------------------------------------------------------------------------------- + + /** + *

Validate that the specified argument character sequence matches the specified regular + * expression pattern; otherwise throwing an exception.

+ * + *
Validate.matchesPattern("hi", "[a-z]*");
+ * + *

The syntax of the pattern is the one used in the {@link Pattern} class.

+ * + * @param input the character sequence to validate, not null + * @param pattern the regular expression pattern, not null + * @throws IllegalArgumentException if the character sequence does not match the pattern + * @see #matchesPattern(CharSequence, String, String, Object...) + * + * @since 3.0 + */ + public static void matchesPattern(CharSequence input, String pattern) { + // TODO when breaking BC, consider returning input + if (Pattern.matches(pattern, input) == false) { + throw new IllegalArgumentException(String.format(DEFAULT_MATCHES_PATTERN_EX, input, pattern)); + } + } + + /** + *

Validate that the specified argument character sequence matches the specified regular + * expression pattern; otherwise throwing an exception with the specified message.

+ * + *
Validate.matchesPattern("hi", "[a-z]*", "%s does not match %s", "hi" "[a-z]*");
+ * + *

The syntax of the pattern is the one used in the {@link Pattern} class.

+ * + * @param input the character sequence to validate, not null + * @param pattern the regular expression pattern, not null + * @param message the {@link String#format(String, Object...)} exception message if invalid, not null + * @param values the optional values for the formatted exception message, null array not recommended + * @throws IllegalArgumentException if the character sequence does not match the pattern + * @see #matchesPattern(CharSequence, String) + * + * @since 3.0 + */ + public static void matchesPattern(CharSequence input, String pattern, String message, Object... values) { + // TODO when breaking BC, consider returning input + if (Pattern.matches(pattern, input) == false) { + throw new IllegalArgumentException(String.format(message, values)); + } + } + + // inclusiveBetween + //--------------------------------------------------------------------------------- + + /** + *

Validate that the specified argument object fall between the two + * inclusive values specified; otherwise, throws an exception.

+ * + *
Validate.inclusiveBetween(0, 2, 1);
+ * + * @param the type of the argument object + * @param start the inclusive start value, not null + * @param end the inclusive end value, not null + * @param value the object to validate, not null + * @throws IllegalArgumentException if the value falls out of the boundaries + * @see #inclusiveBetween(Object, Object, Comparable, String, Object...) + * + * @since 3.0 + */ + public static void inclusiveBetween(T start, T end, Comparable value) { + // TODO when breaking BC, consider returning value + if (value.compareTo(start) < 0 || value.compareTo(end) > 0) { + throw new IllegalArgumentException(String.format(DEFAULT_INCLUSIVE_BETWEEN_EX_MESSAGE, value, start, end)); + } + } + + /** + *

Validate that the specified argument object fall between the two + * inclusive values specified; otherwise, throws an exception with the + * specified message.

+ * + *
Validate.inclusiveBetween(0, 2, 1, "Not in boundaries");
+ * + * @param the type of the argument object + * @param start the inclusive start value, not null + * @param end the inclusive end value, not null + * @param value the object to validate, not null + * @param message the {@link String#format(String, Object...)} exception message if invalid, not null + * @param values the optional values for the formatted exception message, null array not recommended + * @throws IllegalArgumentException if the value falls out of the boundaries + * @see #inclusiveBetween(Object, Object, Comparable) + * + * @since 3.0 + */ + public static void inclusiveBetween(T start, T end, Comparable value, String message, Object... values) { + // TODO when breaking BC, consider returning value + if (value.compareTo(start) < 0 || value.compareTo(end) > 0) { + throw new IllegalArgumentException(String.format(message, values)); + } + } + + // exclusiveBetween + //--------------------------------------------------------------------------------- + + /** + *

Validate that the specified argument object fall between the two + * exclusive values specified; otherwise, throws an exception.

+ * + *
Validate.inclusiveBetween(0, 2, 1);
+ * + * @param the type of the argument object + * @param start the exclusive start value, not null + * @param end the exclusive end value, not null + * @param value the object to validate, not null + * @throws IllegalArgumentException if the value falls out of the boundaries + * @see #exclusiveBetween(Object, Object, Comparable, String, Object...) + * + * @since 3.0 + */ + public static void exclusiveBetween(T start, T end, Comparable value) { + // TODO when breaking BC, consider returning value + if (value.compareTo(start) <= 0 || value.compareTo(end) >= 0) { + throw new IllegalArgumentException(String.format(DEFAULT_EXCLUSIVE_BETWEEN_EX_MESSAGE, value, start, end)); + } + } + + /** + *

Validate that the specified argument object fall between the two + * exclusive values specified; otherwise, throws an exception with the + * specified message.

+ * + *
Validate.inclusiveBetween(0, 2, 1, "Not in boundaries");
+ * + * @param the type of the argument object + * @param start the exclusive start value, not null + * @param end the exclusive end value, not null + * @param value the object to validate, not null + * @param message the {@link String#format(String, Object...)} exception message if invalid, not null + * @param values the optional values for the formatted exception message, null array not recommended + * @throws IllegalArgumentException if the value falls out of the boundaries + * @see #exclusiveBetween(Object, Object, Comparable) + * + * @since 3.0 + */ + public static void exclusiveBetween(T start, T end, Comparable value, String message, Object... values) { + // TODO when breaking BC, consider returning value + if (value.compareTo(start) <= 0 || value.compareTo(end) >= 0) { + throw new IllegalArgumentException(String.format(message, values)); + } + } + + // isInstanceOf + //--------------------------------------------------------------------------------- + + /** + * Validates that the argument is an instance of the specified class, if not throws an exception. + * + *

This method is useful when validating according to an arbitrary class

+ * + *
Validate.isInstanceOf(OkClass.class, object);
+ * + *

The message of the exception is "Expected type: {type}, actual: {obj_type}"

+ * + * @param type the class the object must be validated against, not null + * @param obj the object to check, null throws an exception + * @throws IllegalArgumentException if argument is not of specified class + * @see #isInstanceOf(Class, Object, String, Object...) + * + * @since 3.0 + */ + public static void isInstanceOf(Class type, Object obj) { + // TODO when breaking BC, consider returning obj + if (type.isInstance(obj) == false) { + throw new IllegalArgumentException(String.format(DEFAULT_IS_INSTANCE_OF_EX_MESSAGE, type.getName(), + obj == null ? "null" : obj.getClass().getName())); + } + } + + /** + *

Validate that the argument is an instance of the specified class; otherwise + * throwing an exception with the specified message. This method is useful when + * validating according to an arbitrary class

+ * + *
Validate.isInstanceOf(OkClass.classs, object, "Wrong class, object is of class %s",
+     *   object.getClass().getName());
+ * + * @param type the class the object must be validated against, not null + * @param obj the object to check, null throws an exception + * @param message the {@link String#format(String, Object...)} exception message if invalid, not null + * @param values the optional values for the formatted exception message, null array not recommended + * @throws IllegalArgumentException if argument is not of specified class + * @see #isInstanceOf(Class, Object) + * + * @since 3.0 + */ + public static void isInstanceOf(Class type, Object obj, String message, Object... values) { + // TODO when breaking BC, consider returning obj + if (type.isInstance(obj) == false) { + throw new IllegalArgumentException(String.format(message, values)); + } + } + + // isAssignableFrom + //--------------------------------------------------------------------------------- + + /** + * Validates that the argument can be converted to the specified class, if not, throws an exception. + * + *

This method is useful when validating that there will be no casting errors.

+ * + *
Validate.isAssignableFrom(SuperClass.class, object.getClass());
+ * + *

The message format of the exception is "Cannot assign {type} to {superType}"

+ * + * @param superType the class the class must be validated against, not null + * @param type the class to check, not null + * @throws IllegalArgumentException if type argument is not assignable to the specified superType + * @see #isAssignableFrom(Class, Class, String, Object...) + * + * @since 3.0 + */ + public static void isAssignableFrom(Class superType, Class type) { + // TODO when breaking BC, consider returning type + if (superType.isAssignableFrom(type) == false) { + throw new IllegalArgumentException(String.format(DEFAULT_IS_ASSIGNABLE_EX_MESSAGE, type == null ? "null" : type.getName(), + superType.getName())); + } + } + + /** + * Validates that the argument can be converted to the specified class, if not throws an exception. + * + *

This method is useful when validating if there will be no casting errors.

+ * + *
Validate.isAssignableFrom(SuperClass.class, object.getClass());
+ * + *

The message of the exception is "The validated object can not be converted to the" + * followed by the name of the class and "class"

+ * + * @param superType the class the class must be validated against, not null + * @param type the class to check, not null + * @param message the {@link String#format(String, Object...)} exception message if invalid, not null + * @param values the optional values for the formatted exception message, null array not recommended + * @throws IllegalArgumentException if argument can not be converted to the specified class + * @see #isAssignableFrom(Class, Class) + */ + public static void isAssignableFrom(Class superType, Class type, String message, Object... values) { + // TODO when breaking BC, consider returning type + if (superType.isAssignableFrom(type) == false) { + throw new IllegalArgumentException(String.format(message, values)); + } + } +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/ValidateTest.java b/ApacheCommonsLang/org/apache/commons/lang3/ValidateTest.java new file mode 100644 index 0000000..614e953 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/ValidateTest.java @@ -0,0 +1,899 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.commons.lang3; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Modifier; +import java.util.AbstractList; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import junit.framework.TestCase; + +/** + * Unit tests {@link org.apache.commons.lang3.Validate}. + * + * @version $Id: ValidateTest.java 1199726 2011-11-09 12:59:24Z sebb $ + */ +public class ValidateTest extends TestCase { + + public ValidateTest(String name) { + super(name); + } + + //----------------------------------------------------------------------- + public void testIsTrue1() { + Validate.isTrue(true); + try { + Validate.isTrue(false); + fail("Expecting IllegalArgumentException"); + } catch (IllegalArgumentException ex) { + assertEquals("The validated expression is false", ex.getMessage()); + } + } + + //----------------------------------------------------------------------- + public void testIsTrue2() { + Validate.isTrue(true, "MSG"); + try { + Validate.isTrue(false, "MSG"); + fail("Expecting IllegalArgumentException"); + } catch (IllegalArgumentException ex) { + assertEquals("MSG", ex.getMessage()); + } + } + + //----------------------------------------------------------------------- + public void testIsTrue3() { + Validate.isTrue(true, "MSG", 6); + try { + Validate.isTrue(false, "MSG", 6); + fail("Expecting IllegalArgumentException"); + } catch (IllegalArgumentException ex) { + assertEquals("MSG", ex.getMessage()); + } + } + + //----------------------------------------------------------------------- + public void testIsTrue4() { + Validate.isTrue(true, "MSG", 7); + try { + Validate.isTrue(false, "MSG", 7); + fail("Expecting IllegalArgumentException"); + } catch (IllegalArgumentException ex) { + assertEquals("MSG", ex.getMessage()); + } + } + + //----------------------------------------------------------------------- + public void testIsTrue5() { + Validate.isTrue(true, "MSG", 7.4d); + try { + Validate.isTrue(false, "MSG", 7.4d); + fail("Expecting IllegalArgumentException"); + } catch (IllegalArgumentException ex) { + assertEquals("MSG", ex.getMessage()); + } + } + + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + public void testNotNull1() { + Validate.notNull(new Object()); + try { + Validate.notNull(null); + fail("Expecting NullPointerException"); + } catch (NullPointerException ex) { + assertEquals("The validated object is null", ex.getMessage()); + } + + String str = "Hi"; + String testStr = Validate.notNull(str); + assertSame(str, testStr); + } + + //----------------------------------------------------------------------- + public void testNotNull2() { + Validate.notNull(new Object(), "MSG"); + try { + Validate.notNull(null, "MSG"); + fail("Expecting NullPointerException"); + } catch (NullPointerException ex) { + assertEquals("MSG", ex.getMessage()); + } + + String str = "Hi"; + String testStr = Validate.notNull(str, "Message"); + assertSame(str, testStr); + } + + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + public void testNotEmptyArray1() { + Validate.notEmpty(new Object[] {null}); + try { + Validate.notEmpty((Object[]) null); + fail("Expecting NullPointerException"); + } catch (NullPointerException ex) { + assertEquals("The validated array is empty", ex.getMessage()); + } + try { + Validate.notEmpty(new Object[0]); + fail("Expecting IllegalArgumentException"); + } catch (IllegalArgumentException ex) { + assertEquals("The validated array is empty", ex.getMessage()); + } + + String[] array = new String[] {"hi"}; + String[] test = Validate.notEmpty(array); + assertSame(array, test); + } + + //----------------------------------------------------------------------- + public void testNotEmptyArray2() { + Validate.notEmpty(new Object[] {null}, "MSG"); + try { + Validate.notEmpty((Object[]) null, "MSG"); + fail("Expecting NullPointerException"); + } catch (NullPointerException ex) { + assertEquals("MSG", ex.getMessage()); + } + try { + Validate.notEmpty(new Object[0], "MSG"); + fail("Expecting IllegalArgumentException"); + } catch (IllegalArgumentException ex) { + assertEquals("MSG", ex.getMessage()); + } + + String[] array = new String[] {"hi"}; + String[] test = Validate.notEmpty(array, "Message"); + assertSame(array, test); + } + + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + public void testNotEmptyCollection1() { + Collection coll = new ArrayList(); + try { + Validate.notEmpty((Collection) null); + fail("Expecting NullPointerException"); + } catch (NullPointerException ex) { + assertEquals("The validated collection is empty", ex.getMessage()); + } + try { + Validate.notEmpty(coll); + fail("Expecting IllegalArgumentException"); + } catch (IllegalArgumentException ex) { + assertEquals("The validated collection is empty", ex.getMessage()); + } + coll.add(Integer.valueOf(8)); + Validate.notEmpty(coll); + + Collection test = Validate.notEmpty(coll); + assertSame(coll, test); + } + + //----------------------------------------------------------------------- + public void testNotEmptyCollection2() { + Collection coll = new ArrayList(); + try { + Validate.notEmpty((Collection) null, "MSG"); + fail("Expecting NullPointerException"); + } catch (NullPointerException ex) { + assertEquals("MSG", ex.getMessage()); + } + try { + Validate.notEmpty(coll, "MSG"); + fail("Expecting IllegalArgumentException"); + } catch (IllegalArgumentException ex) { + assertEquals("MSG", ex.getMessage()); + } + coll.add(Integer.valueOf(8)); + Validate.notEmpty(coll, "MSG"); + + Collection test = Validate.notEmpty(coll, "Message"); + assertSame(coll, test); + } + + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + public void testNotEmptyMap1() { + Map map = new HashMap(); + try { + Validate.notEmpty((Map) null); + fail("Expecting NullPointerException"); + } catch (NullPointerException ex) { + assertEquals("The validated map is empty", ex.getMessage()); + } + try { + Validate.notEmpty(map); + fail("Expecting IllegalArgumentException"); + } catch (IllegalArgumentException ex) { + assertEquals("The validated map is empty", ex.getMessage()); + } + map.put("ll", Integer.valueOf(8)); + Validate.notEmpty(map); + + Map test = Validate.notEmpty(map); + assertSame(map, test); + } + + //----------------------------------------------------------------------- + public void testNotEmptyMap2() { + Map map = new HashMap(); + try { + Validate.notEmpty((Map) null, "MSG"); + fail("Expecting NullPointerException"); + } catch (NullPointerException ex) { + assertEquals("MSG", ex.getMessage()); + } + try { + Validate.notEmpty(map, "MSG"); + fail("Expecting IllegalArgumentException"); + } catch (IllegalArgumentException ex) { + assertEquals("MSG", ex.getMessage()); + } + map.put("ll", Integer.valueOf(8)); + Validate.notEmpty(map, "MSG"); + + Map test = Validate.notEmpty(map, "Message"); + assertSame(map, test); + } + + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + public void testNotEmptyString1() { + Validate.notEmpty("hjl"); + try { + Validate.notEmpty((String) null); + fail("Expecting NullPointerException"); + } catch (NullPointerException ex) { + assertEquals("The validated character sequence is empty", ex.getMessage()); + } + try { + Validate.notEmpty(""); + fail("Expecting IllegalArgumentException"); + } catch (IllegalArgumentException ex) { + assertEquals("The validated character sequence is empty", ex.getMessage()); + } + + String str = "Hi"; + String testStr = Validate.notEmpty(str); + assertSame(str, testStr); + } + + //----------------------------------------------------------------------- + public void testNotEmptyString2() { + Validate.notEmpty("a", "MSG"); + try { + Validate.notEmpty((String) null, "MSG"); + fail("Expecting NullPointerException"); + } catch (NullPointerException ex) { + assertEquals("MSG", ex.getMessage()); + } + try { + Validate.notEmpty("", "MSG"); + fail("Expecting IllegalArgumentException"); + } catch (IllegalArgumentException ex) { + assertEquals("MSG", ex.getMessage()); + } + + String str = "Hi"; + String testStr = Validate.notEmpty(str, "Message"); + assertSame(str, testStr); + } + + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + public void testNotBlankNullStringShouldThrow() { + //given + String string = null; + + try { + //when + Validate.notBlank(string); + fail("Expecting NullPointerException"); + } catch (NullPointerException e) { + //then + assertEquals("The validated character sequence is blank", e.getMessage()); + } + } + + //----------------------------------------------------------------------- + public void testNotBlankMsgNullStringShouldThrow() { + //given + String string = null; + + try { + //when + Validate.notBlank(string, "Message"); + fail("Expecting NullPointerException"); + } catch (NullPointerException e) { + //then + assertEquals("Message", e.getMessage()); + } + } + + //----------------------------------------------------------------------- + public void testNotBlankEmptyStringShouldThrow() { + //given + String string = ""; + + try { + //when + Validate.notBlank(string); + fail("Expecting IllegalArgumentException"); + } catch (IllegalArgumentException e) { + //then + assertEquals("The validated character sequence is blank", e.getMessage()); + } + } + + //----------------------------------------------------------------------- + public void testNotBlankBlankStringWithWhitespacesShouldThrow() { + //given + String string = " "; + + try { + //when + Validate.notBlank(string); + fail("Expecting IllegalArgumentException"); + } catch (IllegalArgumentException e) { + //then + assertEquals("The validated character sequence is blank", e.getMessage()); + } + } + + //----------------------------------------------------------------------- + public void testNotBlankBlankStringWithNewlinesShouldThrow() { + //given + String string = " \n \t \r \n "; + + try { + //when + Validate.notBlank(string); + fail("Expecting IllegalArgumentException"); + } catch (IllegalArgumentException e) { + //then + assertEquals("The validated character sequence is blank", e.getMessage()); + } + } + + //----------------------------------------------------------------------- + public void testNotBlankMsgBlankStringShouldThrow() { + //given + String string = " \n \t \r \n "; + + try { + //when + Validate.notBlank(string, "Message"); + fail("Expecting IllegalArgumentException"); + } catch (IllegalArgumentException e) { + //then + assertEquals("Message", e.getMessage()); + } + } + + //----------------------------------------------------------------------- + public void testNotBlankMsgBlankStringWithWhitespacesShouldThrow() { + //given + String string = " "; + + try { + //when + Validate.notBlank(string, "Message"); + fail("Expecting IllegalArgumentException"); + } catch (IllegalArgumentException e) { + //then + assertEquals("Message", e.getMessage()); + } + } + + //----------------------------------------------------------------------- + public void testNotBlankMsgEmptyStringShouldThrow() { + //given + String string = ""; + + try { + //when + Validate.notBlank(string, "Message"); + fail("Expecting IllegalArgumentException"); + } catch (IllegalArgumentException e) { + //then + assertEquals("Message", e.getMessage()); + } + } + + //----------------------------------------------------------------------- + public void testNotBlankNotBlankStringShouldNotThrow() { + //given + String string = "abc"; + + //when + Validate.notBlank(string); + + //then should not throw + } + + //----------------------------------------------------------------------- + public void testNotBlankNotBlankStringWithWhitespacesShouldNotThrow() { + //given + String string = " abc "; + + //when + Validate.notBlank(string); + + //then should not throw + } + + //----------------------------------------------------------------------- + public void testNotBlankNotBlankStringWithNewlinesShouldNotThrow() { + //given + String string = " \n \t abc \r \n "; + + //when + Validate.notBlank(string); + + //then should not throw + } + + //----------------------------------------------------------------------- + public void testNotBlankMsgNotBlankStringShouldNotThrow() { + //given + String string = "abc"; + + //when + Validate.notBlank(string, "Message"); + + //then should not throw + } + + //----------------------------------------------------------------------- + public void testNotBlankMsgNotBlankStringWithWhitespacesShouldNotThrow() { + //given + String string = " abc "; + + //when + Validate.notBlank(string, "Message"); + + //then should not throw + } + + //----------------------------------------------------------------------- + public void testNotBlankMsgNotBlankStringWithNewlinesShouldNotThrow() { + //given + String string = " \n \t abc \r \n "; + + //when + Validate.notBlank(string, "Message"); + + //then should not throw + } + + //----------------------------------------------------------------------- + public void testNotBlankReturnValues1() { + String str = "Hi"; + String test = Validate.notBlank(str); + assertSame(str, test); + } + + public void testNotBlankReturnValues2() { + String str = "Hi"; + String test = Validate.notBlank(str, "Message"); + assertSame(str, test); + } + + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + public void testNoNullElementsArray1() { + String[] array = new String[] {"a", "b"}; + Validate.noNullElements(array); + try { + Validate.noNullElements((Object[]) null); + fail("Expecting NullPointerException"); + } catch (NullPointerException ex) { + assertEquals("The validated object is null", ex.getMessage()); + } + array[1] = null; + try { + Validate.noNullElements(array); + fail("Expecting IllegalArgumentException"); + } catch (IllegalArgumentException ex) { + assertEquals("The validated array contains null element at index: 1", ex.getMessage()); + } + + array = new String[] {"a", "b"}; + String[] test = Validate.noNullElements(array); + assertSame(array, test); + } + + //----------------------------------------------------------------------- + public void testNoNullElementsArray2() { + String[] array = new String[] {"a", "b"}; + Validate.noNullElements(array, "MSG"); + try { + Validate.noNullElements((Object[]) null, "MSG"); + fail("Expecting NullPointerException"); + } catch (NullPointerException ex) { + assertEquals("The validated object is null", ex.getMessage()); + } + array[1] = null; + try { + Validate.noNullElements(array, "MSG"); + fail("Expecting IllegalArgumentException"); + } catch (IllegalArgumentException ex) { + assertEquals("MSG", ex.getMessage()); + } + + array = new String[] {"a", "b"}; + String[] test = Validate.noNullElements(array, "Message"); + assertSame(array, test); + } + + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + public void testNoNullElementsCollection1() { + List coll = new ArrayList(); + coll.add("a"); + coll.add("b"); + Validate.noNullElements(coll); + try { + Validate.noNullElements((Collection) null); + fail("Expecting NullPointerException"); + } catch (NullPointerException ex) { + assertEquals("The validated object is null", ex.getMessage()); + } + coll.set(1, null); + try { + Validate.noNullElements(coll); + fail("Expecting IllegalArgumentException"); + } catch (IllegalArgumentException ex) { + assertEquals("The validated collection contains null element at index: 1", ex.getMessage()); + } + + coll.set(1, "b"); + List test = Validate.noNullElements(coll); + assertSame(coll, test); + } + + //----------------------------------------------------------------------- + public void testNoNullElementsCollection2() { + List coll = new ArrayList(); + coll.add("a"); + coll.add("b"); + Validate.noNullElements(coll, "MSG"); + try { + Validate.noNullElements((Collection) null, "MSG"); + fail("Expecting NullPointerException"); + } catch (NullPointerException ex) { + assertEquals("The validated object is null", ex.getMessage()); + } + coll.set(1, null); + try { + Validate.noNullElements(coll, "MSG"); + fail("Expecting IllegalArgumentException"); + } catch (IllegalArgumentException ex) { + assertEquals("MSG", ex.getMessage()); + } + + coll.set(1, "b"); + List test = Validate.noNullElements(coll, "Message"); + assertSame(coll, test); + } + + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + public void testConstructor() { + assertNotNull(new Validate()); + Constructor[] cons = Validate.class.getDeclaredConstructors(); + assertEquals(1, cons.length); + assertEquals(true, Modifier.isPublic(cons[0].getModifiers())); + assertEquals(true, Modifier.isPublic(Validate.class.getModifiers())); + assertEquals(false, Modifier.isFinal(Validate.class.getModifiers())); + } + + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + public void testValidIndex_withMessage_array() { + Object[] array = new Object[2]; + Validate.validIndex(array, 0, "Broken: "); + Validate.validIndex(array, 1, "Broken: "); + try { + Validate.validIndex(array, -1, "Broken: "); + fail("Expecting IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException ex) { + assertEquals("Broken: ", ex.getMessage()); + } + try { + Validate.validIndex(array, 2, "Broken: "); + fail("Expecting IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException ex) { + assertEquals("Broken: ", ex.getMessage()); + } + + String[] strArray = new String[] {"Hi"}; + String[] test = Validate.noNullElements(strArray, "Message"); + assertSame(strArray, test); + } + + public void testValidIndex_array() { + Object[] array = new Object[2]; + Validate.validIndex(array, 0); + Validate.validIndex(array, 1); + try { + Validate.validIndex(array, -1); + fail("Expecting IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException ex) { + assertEquals("The validated array index is invalid: -1", ex.getMessage()); + } + try { + Validate.validIndex(array, 2); + fail("Expecting IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException ex) { + assertEquals("The validated array index is invalid: 2", ex.getMessage()); + } + + String[] strArray = new String[] {"Hi"}; + String[] test = Validate.noNullElements(strArray); + assertSame(strArray, test); + } + + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + public void testValidIndex_withMessage_collection() { + Collection coll = new ArrayList(); + coll.add(null); + coll.add(null); + Validate.validIndex(coll, 0, "Broken: "); + Validate.validIndex(coll, 1, "Broken: "); + try { + Validate.validIndex(coll, -1, "Broken: "); + fail("Expecting IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException ex) { + assertEquals("Broken: ", ex.getMessage()); + } + try { + Validate.validIndex(coll, 2, "Broken: "); + fail("Expecting IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException ex) { + assertEquals("Broken: ", ex.getMessage()); + } + + List strColl = Arrays.asList(new String[] {"Hi"}); + List test = Validate.validIndex(strColl, 0, "Message"); + assertSame(strColl, test); + } + + public void testValidIndex_collection() { + Collection coll = new ArrayList(); + coll.add(null); + coll.add(null); + Validate.validIndex(coll, 0); + Validate.validIndex(coll, 1); + try { + Validate.validIndex(coll, -1); + fail("Expecting IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException ex) { + assertEquals("The validated collection index is invalid: -1", ex.getMessage()); + } + try { + Validate.validIndex(coll, 2); + fail("Expecting IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException ex) { + assertEquals("The validated collection index is invalid: 2", ex.getMessage()); + } + + List strColl = Arrays.asList(new String[] {"Hi"}); + List test = Validate.validIndex(strColl, 0); + assertSame(strColl, test); + } + + //----------------------------------------------------------------------- + //----------------------------------------------------------------------- + public void testValidIndex_withMessage_charSequence() { + CharSequence str = "Hi"; + Validate.validIndex(str, 0, "Broken: "); + Validate.validIndex(str, 1, "Broken: "); + try { + Validate.validIndex(str, -1, "Broken: "); + fail("Expecting IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException ex) { + assertEquals("Broken: ", ex.getMessage()); + } + try { + Validate.validIndex(str, 2, "Broken: "); + fail("Expecting IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException ex) { + assertEquals("Broken: ", ex.getMessage()); + } + + String input = "Hi"; + String test = Validate.validIndex(input, 0, "Message"); + assertSame(input, test); + } + + public void testValidIndex_charSequence() { + CharSequence str = "Hi"; + Validate.validIndex(str, 0); + Validate.validIndex(str, 1); + try { + Validate.validIndex(str, -1); + fail("Expecting IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException ex) { + assertEquals("The validated character sequence index is invalid: -1", ex.getMessage()); + } + try { + Validate.validIndex(str, 2); + fail("Expecting IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException ex) { + assertEquals("The validated character sequence index is invalid: 2", ex.getMessage()); + } + + String input = "Hi"; + String test = Validate.validIndex(input, 0); + assertSame(input, test); + } + + public void testMatchesPattern() + { + CharSequence str = "hi"; + Validate.matchesPattern(str, "[a-z]*"); + try + { + Validate.matchesPattern(str, "[0-9]*"); + fail("Expecting IllegalArgumentException"); + } + catch (IllegalArgumentException e) + { + assertEquals("The string hi does not match the pattern [0-9]*", e.getMessage()); + } + } + + public void testMatchesPattern_withMessage() + { + CharSequence str = "hi"; + Validate.matchesPattern(str, "[a-z]*", "Does not match"); + try + { + Validate.matchesPattern(str, "[0-9]*", "Does not match"); + fail("Expecting IllegalArgumentException"); + } + catch (IllegalArgumentException e) + { + assertEquals("Does not match", e.getMessage()); + } + } + + public void testInclusiveBetween() + { + Validate.inclusiveBetween("a", "c", "b"); + Validate.inclusiveBetween(0, 2, 1); + Validate.inclusiveBetween(0, 2, 2); + try { + Validate.inclusiveBetween(0, 5, 6); + fail("Expecting IllegalArgumentException"); + } catch (IllegalArgumentException e) { + assertEquals("The value 6 is not in the specified inclusive range of 0 to 5", e.getMessage()); + } + } + + public void testInclusiveBetween_withMessage() + { + Validate.inclusiveBetween("a", "c", "b", "Error"); + Validate.inclusiveBetween(0, 2, 1, "Error"); + Validate.inclusiveBetween(0, 2, 2, "Error"); + try { + Validate.inclusiveBetween(0, 5, 6, "Error"); + fail("Expecting IllegalArgumentException"); + } catch (IllegalArgumentException e) { + assertEquals("Error", e.getMessage()); + } + } + + public void testExclusiveBetween() + { + Validate.exclusiveBetween("a", "c", "b"); + Validate.exclusiveBetween(0, 2, 1); + try { + Validate.exclusiveBetween(0, 5, 6); + fail("Expecting IllegalArgumentException"); + } catch (IllegalArgumentException e) { + assertEquals("The value 6 is not in the specified exclusive range of 0 to 5", e.getMessage()); + } + try { + Validate.exclusiveBetween(0, 5, 5); + fail("Expecting IllegalArgumentException"); + } catch (IllegalArgumentException e) { + assertEquals("The value 5 is not in the specified exclusive range of 0 to 5", e.getMessage()); + } + } + + public void testExclusiveBetween_withMessage() + { + Validate.exclusiveBetween("a", "c", "b", "Error"); + Validate.exclusiveBetween(0, 2, 1, "Error"); + try { + Validate.exclusiveBetween(0, 5, 6, "Error"); + fail("Expecting IllegalArgumentException"); + } catch (IllegalArgumentException e) { + assertEquals("Error", e.getMessage()); + } + try { + Validate.exclusiveBetween(0, 5, 5, "Error"); + fail("Expecting IllegalArgumentException"); + } catch (IllegalArgumentException e) { + assertEquals("Error", e.getMessage()); + } + } + + public void testIsInstanceOf() { + Validate.isInstanceOf(String.class, "hi"); + Validate.isInstanceOf(Integer.class, 1); + } + + public void testIsInstanceOfExceptionMessage() { + try { + Validate.isInstanceOf(List.class, "hi"); + fail("Expecting IllegalArgumentException"); + } catch(IllegalArgumentException e) { + assertEquals("Expected type: java.util.List, actual: java.lang.String", e.getMessage()); + } + } + + public void testIsInstanceOf_withMessage() { + Validate.isInstanceOf(String.class, "hi", "Error"); + Validate.isInstanceOf(Integer.class, 1, "Error"); + try { + Validate.isInstanceOf(List.class, "hi", "Error"); + fail("Expecting IllegalArgumentException"); + } catch(IllegalArgumentException e) { + assertEquals("Error", e.getMessage()); + } + } + + public void testIsAssignable() { + Validate.isAssignableFrom(CharSequence.class, String.class); + Validate.isAssignableFrom(AbstractList.class, ArrayList.class); + } + + public void testIsAssignableExceptionMessage() { + try { + Validate.isAssignableFrom(List.class, String.class); + fail("Expecting IllegalArgumentException"); + } catch(IllegalArgumentException e) { + assertEquals("Cannot assign a java.lang.String to a java.util.List", e.getMessage()); + } + } + + public void testIsAssignable_withMessage() { + Validate.isAssignableFrom(CharSequence.class, String.class, "Error"); + Validate.isAssignableFrom(AbstractList.class, ArrayList.class, "Error"); + try { + Validate.isAssignableFrom(List.class, String.class, "Error"); + fail("Expecting IllegalArgumentException"); + } catch(IllegalArgumentException e) { + assertEquals("Error", e.getMessage()); + } + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/builder/Builder.java b/ApacheCommonsLang/org/apache/commons/lang3/builder/Builder.java new file mode 100644 index 0000000..ce696e8 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/builder/Builder.java @@ -0,0 +1,89 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.builder; + +/** + *

+ * The Builder interface is designed to designate a class as a builder + * object in the Builder design pattern. Builders are capable of creating and + * configuring objects or results that normally take multiple steps to construct + * or are very complex to derive. + *

+ * + *

+ * The builder interface defines a single method, {@link #build()}, that + * classes must implement. The result of this method should be the final + * configured object or result after all building operations are performed. + *

+ * + *

+ * It is a recommended practice that the methods supplied to configure the + * object or result being built return a reference to {@code this} so that + * method calls can be chained together. + *

+ * + *

+ * Example Builder: + *

+ * class FontBuilder implements Builder<Font> {
+ *     private Font font;
+ *     
+ *     public FontBuilder(String fontName) {
+ *         this.font = new Font(fontName, Font.PLAIN, 12);
+ *     }
+ * 
+ *     public FontBuilder bold() {
+ *         this.font = this.font.deriveFont(Font.BOLD);
+ *         return this; // Reference returned so calls can be chained
+ *     }
+ *     
+ *     public FontBuilder size(float pointSize) {
+ *         this.font = this.font.deriveFont(pointSize);
+ *         return this; // Reference returned so calls can be chained
+ *     }
+ * 
+ *     // Other Font construction methods
+ * 
+ *     public Font build() {
+ *         return this.font;
+ *     }
+ * }
+ * 
+ * + * Example Builder Usage: + *
+ * Font bold14ptSansSerifFont = new FontBuilder(Font.SANS_SERIF).bold()
+ *                                                              .size(14.0f)
+ *                                                              .build();
+ * 
+ *

+ * + * @param the type of object that the builder will construct or compute. + * + * @since 3.0 + * @version $Id: Builder.java 1088899 2011-04-05 05:31:27Z bayard $ + */ +public interface Builder { + + /** + * Returns a reference to the object being constructed or result being + * calculated by the builder. + * + * @return the object constructed or result calculated by the builder. + */ + public T build(); +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/builder/CompareToBuilder.java b/ApacheCommonsLang/org/apache/commons/lang3/builder/CompareToBuilder.java new file mode 100644 index 0000000..84163b3 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/builder/CompareToBuilder.java @@ -0,0 +1,1021 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.builder; + +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.Collection; +import java.util.Comparator; + +import org.apache.commons.lang3.ArrayUtils; + +/** + * Assists in implementing {@link java.lang.Comparable#compareTo(Object)} methods. + * + * It is consistent with equals(Object) and + * hashcode() built with {@link EqualsBuilder} and + * {@link HashCodeBuilder}.

+ * + *

Two Objects that compare equal using equals(Object) should normally + * also compare equal using compareTo(Object).

+ * + *

All relevant fields should be included in the calculation of the + * comparison. Derived fields may be ignored. The same fields, in the same + * order, should be used in both compareTo(Object) and + * equals(Object).

+ * + *

To use this class write code as follows:

+ * + *
+ * public class MyClass {
+ *   String field1;
+ *   int field2;
+ *   boolean field3;
+ *
+ *   ...
+ *
+ *   public int compareTo(Object o) {
+ *     MyClass myClass = (MyClass) o;
+ *     return new CompareToBuilder()
+ *       .appendSuper(super.compareTo(o)
+ *       .append(this.field1, myClass.field1)
+ *       .append(this.field2, myClass.field2)
+ *       .append(this.field3, myClass.field3)
+ *       .toComparison();
+ *   }
+ * }
+ * 
+ * + *

Alternatively, there are {@link #reflectionCompare(Object, Object) reflectionCompare} methods that use + * reflection to determine the fields to append. Because fields can be private, + * reflectionCompare uses {@link java.lang.reflect.AccessibleObject#setAccessible(boolean)} to + * bypass normal access control checks. This will fail under a security manager, + * unless the appropriate permissions are set up correctly. It is also + * slower than appending explicitly.

+ * + *

A typical implementation of compareTo(Object) using + * reflectionCompare looks like:

+ + *
+ * public int compareTo(Object o) {
+ *   return CompareToBuilder.reflectionCompare(this, o);
+ * }
+ * 
+ * + * @see java.lang.Comparable + * @see java.lang.Object#equals(Object) + * @see java.lang.Object#hashCode() + * @see EqualsBuilder + * @see HashCodeBuilder + * @since 1.0 + * @version $Id: CompareToBuilder.java 1309977 2012-04-05 17:53:39Z ggregory $ + */ +public class CompareToBuilder implements Builder { + + /** + * Current state of the comparison as appended fields are checked. + */ + private int comparison; + + /** + *

Constructor for CompareToBuilder.

+ * + *

Starts off assuming that the objects are equal. Multiple calls are + * then made to the various append methods, followed by a call to + * {@link #toComparison} to get the result.

+ */ + public CompareToBuilder() { + super(); + comparison = 0; + } + + //----------------------------------------------------------------------- + /** + *

Compares two Objects via reflection.

+ * + *

Fields can be private, thus AccessibleObject.setAccessible + * is used to bypass normal access control checks. This will fail under a + * security manager unless the appropriate permissions are set.

+ * + *
    + *
  • Static fields will not be compared
  • + *
  • Transient members will be not be compared, as they are likely derived + * fields
  • + *
  • Superclass fields will be compared
  • + *
+ * + *

If both lhs and rhs are null, + * they are considered equal.

+ * + * @param lhs left-hand object + * @param rhs right-hand object + * @return a negative integer, zero, or a positive integer as lhs + * is less than, equal to, or greater than rhs + * @throws NullPointerException if either (but not both) parameters are + * null + * @throws ClassCastException if rhs is not assignment-compatible + * with lhs + */ + public static int reflectionCompare(Object lhs, Object rhs) { + return reflectionCompare(lhs, rhs, false, null); + } + + /** + *

Compares two Objects via reflection.

+ * + *

Fields can be private, thus AccessibleObject.setAccessible + * is used to bypass normal access control checks. This will fail under a + * security manager unless the appropriate permissions are set.

+ * + *
    + *
  • Static fields will not be compared
  • + *
  • If compareTransients is true, + * compares transient members. Otherwise ignores them, as they + * are likely derived fields.
  • + *
  • Superclass fields will be compared
  • + *
+ * + *

If both lhs and rhs are null, + * they are considered equal.

+ * + * @param lhs left-hand object + * @param rhs right-hand object + * @param compareTransients whether to compare transient fields + * @return a negative integer, zero, or a positive integer as lhs + * is less than, equal to, or greater than rhs + * @throws NullPointerException if either lhs or rhs + * (but not both) is null + * @throws ClassCastException if rhs is not assignment-compatible + * with lhs + */ + public static int reflectionCompare(Object lhs, Object rhs, boolean compareTransients) { + return reflectionCompare(lhs, rhs, compareTransients, null); + } + + /** + *

Compares two Objects via reflection.

+ * + *

Fields can be private, thus AccessibleObject.setAccessible + * is used to bypass normal access control checks. This will fail under a + * security manager unless the appropriate permissions are set.

+ * + *
    + *
  • Static fields will not be compared
  • + *
  • If compareTransients is true, + * compares transient members. Otherwise ignores them, as they + * are likely derived fields.
  • + *
  • Superclass fields will be compared
  • + *
+ * + *

If both lhs and rhs are null, + * they are considered equal.

+ * + * @param lhs left-hand object + * @param rhs right-hand object + * @param excludeFields Collection of String fields to exclude + * @return a negative integer, zero, or a positive integer as lhs + * is less than, equal to, or greater than rhs + * @throws NullPointerException if either lhs or rhs + * (but not both) is null + * @throws ClassCastException if rhs is not assignment-compatible + * with lhs + * @since 2.2 + */ + public static int reflectionCompare(Object lhs, Object rhs, Collection excludeFields) { + return reflectionCompare(lhs, rhs, ReflectionToStringBuilder.toNoNullStringArray(excludeFields)); + } + + /** + *

Compares two Objects via reflection.

+ * + *

Fields can be private, thus AccessibleObject.setAccessible + * is used to bypass normal access control checks. This will fail under a + * security manager unless the appropriate permissions are set.

+ * + *
    + *
  • Static fields will not be compared
  • + *
  • If compareTransients is true, + * compares transient members. Otherwise ignores them, as they + * are likely derived fields.
  • + *
  • Superclass fields will be compared
  • + *
+ * + *

If both lhs and rhs are null, + * they are considered equal.

+ * + * @param lhs left-hand object + * @param rhs right-hand object + * @param excludeFields array of fields to exclude + * @return a negative integer, zero, or a positive integer as lhs + * is less than, equal to, or greater than rhs + * @throws NullPointerException if either lhs or rhs + * (but not both) is null + * @throws ClassCastException if rhs is not assignment-compatible + * with lhs + * @since 2.2 + */ + public static int reflectionCompare(Object lhs, Object rhs, String... excludeFields) { + return reflectionCompare(lhs, rhs, false, null, excludeFields); + } + + /** + *

Compares two Objects via reflection.

+ * + *

Fields can be private, thus AccessibleObject.setAccessible + * is used to bypass normal access control checks. This will fail under a + * security manager unless the appropriate permissions are set.

+ * + *
    + *
  • Static fields will not be compared
  • + *
  • If the compareTransients is true, + * compares transient members. Otherwise ignores them, as they + * are likely derived fields.
  • + *
  • Compares superclass fields up to and including reflectUpToClass. + * If reflectUpToClass is null, compares all superclass fields.
  • + *
+ * + *

If both lhs and rhs are null, + * they are considered equal.

+ * + * @param lhs left-hand object + * @param rhs right-hand object + * @param compareTransients whether to compare transient fields + * @param reflectUpToClass last superclass for which fields are compared + * @param excludeFields fields to exclude + * @return a negative integer, zero, or a positive integer as lhs + * is less than, equal to, or greater than rhs + * @throws NullPointerException if either lhs or rhs + * (but not both) is null + * @throws ClassCastException if rhs is not assignment-compatible + * with lhs + * @since 2.2 (2.0 as reflectionCompare(Object, Object, boolean, Class)) + */ + public static int reflectionCompare( + Object lhs, + Object rhs, + boolean compareTransients, + Class reflectUpToClass, + String... excludeFields) { + + if (lhs == rhs) { + return 0; + } + if (lhs == null || rhs == null) { + throw new NullPointerException(); + } + Class lhsClazz = lhs.getClass(); + if (!lhsClazz.isInstance(rhs)) { + throw new ClassCastException(); + } + CompareToBuilder compareToBuilder = new CompareToBuilder(); + reflectionAppend(lhs, rhs, lhsClazz, compareToBuilder, compareTransients, excludeFields); + while (lhsClazz.getSuperclass() != null && lhsClazz != reflectUpToClass) { + lhsClazz = lhsClazz.getSuperclass(); + reflectionAppend(lhs, rhs, lhsClazz, compareToBuilder, compareTransients, excludeFields); + } + return compareToBuilder.toComparison(); + } + + /** + *

Appends to builder the comparison of lhs + * to rhs using the fields defined in clazz.

+ * + * @param lhs left-hand object + * @param rhs right-hand object + * @param clazz Class that defines fields to be compared + * @param builder CompareToBuilder to append to + * @param useTransients whether to compare transient fields + * @param excludeFields fields to exclude + */ + private static void reflectionAppend( + Object lhs, + Object rhs, + Class clazz, + CompareToBuilder builder, + boolean useTransients, + String[] excludeFields) { + + Field[] fields = clazz.getDeclaredFields(); + AccessibleObject.setAccessible(fields, true); + for (int i = 0; i < fields.length && builder.comparison == 0; i++) { + Field f = fields[i]; + if (!ArrayUtils.contains(excludeFields, f.getName()) + && (f.getName().indexOf('$') == -1) + && (useTransients || !Modifier.isTransient(f.getModifiers())) + && (!Modifier.isStatic(f.getModifiers()))) { + try { + builder.append(f.get(lhs), f.get(rhs)); + } catch (IllegalAccessException e) { + // This can't happen. Would get a Security exception instead. + // Throw a runtime exception in case the impossible happens. + throw new InternalError("Unexpected IllegalAccessException"); + } + } + } + } + + //----------------------------------------------------------------------- + /** + *

Appends to the builder the compareTo(Object) + * result of the superclass.

+ * + * @param superCompareTo result of calling super.compareTo(Object) + * @return this - used to chain append calls + * @since 2.0 + */ + public CompareToBuilder appendSuper(int superCompareTo) { + if (comparison != 0) { + return this; + } + comparison = superCompareTo; + return this; + } + + //----------------------------------------------------------------------- + /** + *

Appends to the builder the comparison of + * two Objects.

+ * + *
    + *
  1. Check if lhs == rhs
  2. + *
  3. Check if either lhs or rhs is null, + * a null object is less than a non-null object
  4. + *
  5. Check the object contents
  6. + *
+ * + *

lhs must either be an array or implement {@link Comparable}.

+ * + * @param lhs left-hand object + * @param rhs right-hand object + * @return this - used to chain append calls + * @throws ClassCastException if rhs is not assignment-compatible + * with lhs + */ + public CompareToBuilder append(Object lhs, Object rhs) { + return append(lhs, rhs, null); + } + + /** + *

Appends to the builder the comparison of + * two Objects.

+ * + *
    + *
  1. Check if lhs == rhs
  2. + *
  3. Check if either lhs or rhs is null, + * a null object is less than a non-null object
  4. + *
  5. Check the object contents
  6. + *
+ * + *

If lhs is an array, array comparison methods will be used. + * Otherwise comparator will be used to compare the objects. + * If comparator is null, lhs must + * implement {@link Comparable} instead.

+ * + * @param lhs left-hand object + * @param rhs right-hand object + * @param comparator Comparator used to compare the objects, + * null means treat lhs as Comparable + * @return this - used to chain append calls + * @throws ClassCastException if rhs is not assignment-compatible + * with lhs + * @since 2.0 + */ + public CompareToBuilder append(Object lhs, Object rhs, Comparator comparator) { + if (comparison != 0) { + return this; + } + if (lhs == rhs) { + return this; + } + if (lhs == null) { + comparison = -1; + return this; + } + if (rhs == null) { + comparison = +1; + return this; + } + if (lhs.getClass().isArray()) { + // switch on type of array, to dispatch to the correct handler + // handles multi dimensional arrays + // throws a ClassCastException if rhs is not the correct array type + if (lhs instanceof long[]) { + append((long[]) lhs, (long[]) rhs); + } else if (lhs instanceof int[]) { + append((int[]) lhs, (int[]) rhs); + } else if (lhs instanceof short[]) { + append((short[]) lhs, (short[]) rhs); + } else if (lhs instanceof char[]) { + append((char[]) lhs, (char[]) rhs); + } else if (lhs instanceof byte[]) { + append((byte[]) lhs, (byte[]) rhs); + } else if (lhs instanceof double[]) { + append((double[]) lhs, (double[]) rhs); + } else if (lhs instanceof float[]) { + append((float[]) lhs, (float[]) rhs); + } else if (lhs instanceof boolean[]) { + append((boolean[]) lhs, (boolean[]) rhs); + } else { + // not an array of primitives + // throws a ClassCastException if rhs is not an array + append((Object[]) lhs, (Object[]) rhs, comparator); + } + } else { + // the simple case, not an array, just test the element + if (comparator == null) { + @SuppressWarnings("unchecked") // assume this can be done; if not throw CCE as per Javadoc + final Comparable comparable = (Comparable) lhs; + comparison = comparable.compareTo(rhs); + } else { + @SuppressWarnings("unchecked") // assume this can be done; if not throw CCE as per Javadoc + final Comparator comparator2 = (Comparator) comparator; + comparison = comparator2.compare(lhs, rhs); + } + } + return this; + } + + //------------------------------------------------------------------------- + /** + * Appends to the builder the comparison of + * two longs. + * + * @param lhs left-hand value + * @param rhs right-hand value + * @return this - used to chain append calls + */ + public CompareToBuilder append(long lhs, long rhs) { + if (comparison != 0) { + return this; + } + comparison = ((lhs < rhs) ? -1 : ((lhs > rhs) ? 1 : 0)); + return this; + } + + /** + * Appends to the builder the comparison of + * two ints. + * + * @param lhs left-hand value + * @param rhs right-hand value + * @return this - used to chain append calls + */ + public CompareToBuilder append(int lhs, int rhs) { + if (comparison != 0) { + return this; + } + comparison = ((lhs < rhs) ? -1 : ((lhs > rhs) ? 1 : 0)); + return this; + } + + /** + * Appends to the builder the comparison of + * two shorts. + * + * @param lhs left-hand value + * @param rhs right-hand value + * @return this - used to chain append calls + */ + public CompareToBuilder append(short lhs, short rhs) { + if (comparison != 0) { + return this; + } + comparison = ((lhs < rhs) ? -1 : ((lhs > rhs) ? 1 : 0)); + return this; + } + + /** + * Appends to the builder the comparison of + * two chars. + * + * @param lhs left-hand value + * @param rhs right-hand value + * @return this - used to chain append calls + */ + public CompareToBuilder append(char lhs, char rhs) { + if (comparison != 0) { + return this; + } + comparison = ((lhs < rhs) ? -1 : ((lhs > rhs) ? 1 : 0)); + return this; + } + + /** + * Appends to the builder the comparison of + * two bytes. + * + * @param lhs left-hand value + * @param rhs right-hand value + * @return this - used to chain append calls + */ + public CompareToBuilder append(byte lhs, byte rhs) { + if (comparison != 0) { + return this; + } + comparison = ((lhs < rhs) ? -1 : ((lhs > rhs) ? 1 : 0)); + return this; + } + + /** + *

Appends to the builder the comparison of + * two doubles.

+ * + *

This handles NaNs, Infinities, and -0.0.

+ * + *

It is compatible with the hash code generated by + * HashCodeBuilder.

+ * + * @param lhs left-hand value + * @param rhs right-hand value + * @return this - used to chain append calls + */ + public CompareToBuilder append(double lhs, double rhs) { + if (comparison != 0) { + return this; + } + comparison = Double.compare(lhs, rhs); + return this; + } + + /** + *

Appends to the builder the comparison of + * two floats.

+ * + *

This handles NaNs, Infinities, and -0.0.

+ * + *

It is compatible with the hash code generated by + * HashCodeBuilder.

+ * + * @param lhs left-hand value + * @param rhs right-hand value + * @return this - used to chain append calls + */ + public CompareToBuilder append(float lhs, float rhs) { + if (comparison != 0) { + return this; + } + comparison = Float.compare(lhs, rhs); + return this; + } + + /** + * Appends to the builder the comparison of + * two booleanss. + * + * @param lhs left-hand value + * @param rhs right-hand value + * @return this - used to chain append calls + */ + public CompareToBuilder append(boolean lhs, boolean rhs) { + if (comparison != 0) { + return this; + } + if (lhs == rhs) { + return this; + } + if (lhs == false) { + comparison = -1; + } else { + comparison = +1; + } + return this; + } + + //----------------------------------------------------------------------- + /** + *

Appends to the builder the deep comparison of + * two Object arrays.

+ * + *
    + *
  1. Check if arrays are the same using ==
  2. + *
  3. Check if for null, null is less than non-null
  4. + *
  5. Check array length, a short length array is less than a long length array
  6. + *
  7. Check array contents element by element using {@link #append(Object, Object, Comparator)}
  8. + *
+ * + *

This method will also will be called for the top level of multi-dimensional, + * ragged, and multi-typed arrays.

+ * + * @param lhs left-hand array + * @param rhs right-hand array + * @return this - used to chain append calls + * @throws ClassCastException if rhs is not assignment-compatible + * with lhs + */ + public CompareToBuilder append(Object[] lhs, Object[] rhs) { + return append(lhs, rhs, null); + } + + /** + *

Appends to the builder the deep comparison of + * two Object arrays.

+ * + *
    + *
  1. Check if arrays are the same using ==
  2. + *
  3. Check if for null, null is less than non-null
  4. + *
  5. Check array length, a short length array is less than a long length array
  6. + *
  7. Check array contents element by element using {@link #append(Object, Object, Comparator)}
  8. + *
+ * + *

This method will also will be called for the top level of multi-dimensional, + * ragged, and multi-typed arrays.

+ * + * @param lhs left-hand array + * @param rhs right-hand array + * @param comparator Comparator to use to compare the array elements, + * null means to treat lhs elements as Comparable. + * @return this - used to chain append calls + * @throws ClassCastException if rhs is not assignment-compatible + * with lhs + * @since 2.0 + */ + public CompareToBuilder append(Object[] lhs, Object[] rhs, Comparator comparator) { + if (comparison != 0) { + return this; + } + if (lhs == rhs) { + return this; + } + if (lhs == null) { + comparison = -1; + return this; + } + if (rhs == null) { + comparison = +1; + return this; + } + if (lhs.length != rhs.length) { + comparison = (lhs.length < rhs.length) ? -1 : +1; + return this; + } + for (int i = 0; i < lhs.length && comparison == 0; i++) { + append(lhs[i], rhs[i], comparator); + } + return this; + } + + /** + *

Appends to the builder the deep comparison of + * two long arrays.

+ * + *
    + *
  1. Check if arrays are the same using ==
  2. + *
  3. Check if for null, null is less than non-null
  4. + *
  5. Check array length, a shorter length array is less than a longer length array
  6. + *
  7. Check array contents element by element using {@link #append(long, long)}
  8. + *
+ * + * @param lhs left-hand array + * @param rhs right-hand array + * @return this - used to chain append calls + */ + public CompareToBuilder append(long[] lhs, long[] rhs) { + if (comparison != 0) { + return this; + } + if (lhs == rhs) { + return this; + } + if (lhs == null) { + comparison = -1; + return this; + } + if (rhs == null) { + comparison = +1; + return this; + } + if (lhs.length != rhs.length) { + comparison = (lhs.length < rhs.length) ? -1 : +1; + return this; + } + for (int i = 0; i < lhs.length && comparison == 0; i++) { + append(lhs[i], rhs[i]); + } + return this; + } + + /** + *

Appends to the builder the deep comparison of + * two int arrays.

+ * + *
    + *
  1. Check if arrays are the same using ==
  2. + *
  3. Check if for null, null is less than non-null
  4. + *
  5. Check array length, a shorter length array is less than a longer length array
  6. + *
  7. Check array contents element by element using {@link #append(int, int)}
  8. + *
+ * + * @param lhs left-hand array + * @param rhs right-hand array + * @return this - used to chain append calls + */ + public CompareToBuilder append(int[] lhs, int[] rhs) { + if (comparison != 0) { + return this; + } + if (lhs == rhs) { + return this; + } + if (lhs == null) { + comparison = -1; + return this; + } + if (rhs == null) { + comparison = +1; + return this; + } + if (lhs.length != rhs.length) { + comparison = (lhs.length < rhs.length) ? -1 : +1; + return this; + } + for (int i = 0; i < lhs.length && comparison == 0; i++) { + append(lhs[i], rhs[i]); + } + return this; + } + + /** + *

Appends to the builder the deep comparison of + * two short arrays.

+ * + *
    + *
  1. Check if arrays are the same using ==
  2. + *
  3. Check if for null, null is less than non-null
  4. + *
  5. Check array length, a shorter length array is less than a longer length array
  6. + *
  7. Check array contents element by element using {@link #append(short, short)}
  8. + *
+ * + * @param lhs left-hand array + * @param rhs right-hand array + * @return this - used to chain append calls + */ + public CompareToBuilder append(short[] lhs, short[] rhs) { + if (comparison != 0) { + return this; + } + if (lhs == rhs) { + return this; + } + if (lhs == null) { + comparison = -1; + return this; + } + if (rhs == null) { + comparison = +1; + return this; + } + if (lhs.length != rhs.length) { + comparison = (lhs.length < rhs.length) ? -1 : +1; + return this; + } + for (int i = 0; i < lhs.length && comparison == 0; i++) { + append(lhs[i], rhs[i]); + } + return this; + } + + /** + *

Appends to the builder the deep comparison of + * two char arrays.

+ * + *
    + *
  1. Check if arrays are the same using ==
  2. + *
  3. Check if for null, null is less than non-null
  4. + *
  5. Check array length, a shorter length array is less than a longer length array
  6. + *
  7. Check array contents element by element using {@link #append(char, char)}
  8. + *
+ * + * @param lhs left-hand array + * @param rhs right-hand array + * @return this - used to chain append calls + */ + public CompareToBuilder append(char[] lhs, char[] rhs) { + if (comparison != 0) { + return this; + } + if (lhs == rhs) { + return this; + } + if (lhs == null) { + comparison = -1; + return this; + } + if (rhs == null) { + comparison = +1; + return this; + } + if (lhs.length != rhs.length) { + comparison = (lhs.length < rhs.length) ? -1 : +1; + return this; + } + for (int i = 0; i < lhs.length && comparison == 0; i++) { + append(lhs[i], rhs[i]); + } + return this; + } + + /** + *

Appends to the builder the deep comparison of + * two byte arrays.

+ * + *
    + *
  1. Check if arrays are the same using ==
  2. + *
  3. Check if for null, null is less than non-null
  4. + *
  5. Check array length, a shorter length array is less than a longer length array
  6. + *
  7. Check array contents element by element using {@link #append(byte, byte)}
  8. + *
+ * + * @param lhs left-hand array + * @param rhs right-hand array + * @return this - used to chain append calls + */ + public CompareToBuilder append(byte[] lhs, byte[] rhs) { + if (comparison != 0) { + return this; + } + if (lhs == rhs) { + return this; + } + if (lhs == null) { + comparison = -1; + return this; + } + if (rhs == null) { + comparison = +1; + return this; + } + if (lhs.length != rhs.length) { + comparison = (lhs.length < rhs.length) ? -1 : +1; + return this; + } + for (int i = 0; i < lhs.length && comparison == 0; i++) { + append(lhs[i], rhs[i]); + } + return this; + } + + /** + *

Appends to the builder the deep comparison of + * two double arrays.

+ * + *
    + *
  1. Check if arrays are the same using ==
  2. + *
  3. Check if for null, null is less than non-null
  4. + *
  5. Check array length, a shorter length array is less than a longer length array
  6. + *
  7. Check array contents element by element using {@link #append(double, double)}
  8. + *
+ * + * @param lhs left-hand array + * @param rhs right-hand array + * @return this - used to chain append calls + */ + public CompareToBuilder append(double[] lhs, double[] rhs) { + if (comparison != 0) { + return this; + } + if (lhs == rhs) { + return this; + } + if (lhs == null) { + comparison = -1; + return this; + } + if (rhs == null) { + comparison = +1; + return this; + } + if (lhs.length != rhs.length) { + comparison = (lhs.length < rhs.length) ? -1 : +1; + return this; + } + for (int i = 0; i < lhs.length && comparison == 0; i++) { + append(lhs[i], rhs[i]); + } + return this; + } + + /** + *

Appends to the builder the deep comparison of + * two float arrays.

+ * + *
    + *
  1. Check if arrays are the same using ==
  2. + *
  3. Check if for null, null is less than non-null
  4. + *
  5. Check array length, a shorter length array is less than a longer length array
  6. + *
  7. Check array contents element by element using {@link #append(float, float)}
  8. + *
+ * + * @param lhs left-hand array + * @param rhs right-hand array + * @return this - used to chain append calls + */ + public CompareToBuilder append(float[] lhs, float[] rhs) { + if (comparison != 0) { + return this; + } + if (lhs == rhs) { + return this; + } + if (lhs == null) { + comparison = -1; + return this; + } + if (rhs == null) { + comparison = +1; + return this; + } + if (lhs.length != rhs.length) { + comparison = (lhs.length < rhs.length) ? -1 : +1; + return this; + } + for (int i = 0; i < lhs.length && comparison == 0; i++) { + append(lhs[i], rhs[i]); + } + return this; + } + + /** + *

Appends to the builder the deep comparison of + * two boolean arrays.

+ * + *
    + *
  1. Check if arrays are the same using ==
  2. + *
  3. Check if for null, null is less than non-null
  4. + *
  5. Check array length, a shorter length array is less than a longer length array
  6. + *
  7. Check array contents element by element using {@link #append(boolean, boolean)}
  8. + *
+ * + * @param lhs left-hand array + * @param rhs right-hand array + * @return this - used to chain append calls + */ + public CompareToBuilder append(boolean[] lhs, boolean[] rhs) { + if (comparison != 0) { + return this; + } + if (lhs == rhs) { + return this; + } + if (lhs == null) { + comparison = -1; + return this; + } + if (rhs == null) { + comparison = +1; + return this; + } + if (lhs.length != rhs.length) { + comparison = (lhs.length < rhs.length) ? -1 : +1; + return this; + } + for (int i = 0; i < lhs.length && comparison == 0; i++) { + append(lhs[i], rhs[i]); + } + return this; + } + + //----------------------------------------------------------------------- + /** + * Returns a negative integer, a positive integer, or zero as + * the builder has judged the "left-hand" side + * as less than, greater than, or equal to the "right-hand" + * side. + * + * @return final comparison result + * @see #build() + */ + public int toComparison() { + return comparison; + } + + /** + * Returns a negative Integer, a positive Integer, or zero as + * the builder has judged the "left-hand" side + * as less than, greater than, or equal to the "right-hand" + * side. + * + * @return final comparison result as an Integer + * @see #toComparison() + * @since 3.0 + */ + @Override + public Integer build() { + return Integer.valueOf(toComparison()); + } +} + diff --git a/ApacheCommonsLang/org/apache/commons/lang3/builder/CompareToBuilderTest.java b/ApacheCommonsLang/org/apache/commons/lang3/builder/CompareToBuilderTest.java new file mode 100644 index 0000000..6decd8a --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/builder/CompareToBuilderTest.java @@ -0,0 +1,1176 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.builder; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.math.BigInteger; + +import org.apache.commons.lang3.builder.CompareToBuilder; +import org.apache.commons.lang3.builder.EqualsBuilder; +import org.junit.Test; + +/** + * Unit tests {@link org.apache.commons.lang3.builder.CompareToBuilder}. + * + * @version $Id: CompareToBuilderTest.java 1309977 2012-04-05 17:53:39Z ggregory $ + */ +public class CompareToBuilderTest { + + //----------------------------------------------------------------------- + + static class TestObject implements Comparable { + private int a; + public TestObject(int a) { + this.a = a; + } + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (!(o instanceof TestObject)) { + return false; + } + TestObject rhs = (TestObject) o; + return a == rhs.a; + } + + public void setA(int a) { + this.a = a; + } + + public int getA() { + return a; + } + @Override + public int compareTo(TestObject rhs) { + return a < rhs.a ? -1 : a > rhs.a ? +1 : 0; + } + } + + static class TestSubObject extends TestObject { + private int b; + public TestSubObject() { + super(0); + } + public TestSubObject(int a, int b) { + super(a); + this.b = b; + } + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (!(o instanceof TestSubObject)) { + return false; + } + TestSubObject rhs = (TestSubObject) o; + return super.equals(o) && b == rhs.b; + } + } + + static class TestTransientSubObject extends TestObject { + @SuppressWarnings("unused") + private transient int t; + public TestTransientSubObject(int a, int t) { + super(a); + this.t = t; + } + } + + @Test + public void testReflectionCompare() { + TestObject o1 = new TestObject(4); + TestObject o2 = new TestObject(4); + assertTrue(CompareToBuilder.reflectionCompare(o1, o1) == 0); + assertTrue(CompareToBuilder.reflectionCompare(o1, o2) == 0); + o2.setA(5); + assertTrue(CompareToBuilder.reflectionCompare(o1, o2) < 0); + assertTrue(CompareToBuilder.reflectionCompare(o2, o1) > 0); + } + + @Test + public void testReflectionCompareEx1() { + TestObject o1 = new TestObject(4); + try { + CompareToBuilder.reflectionCompare(o1, null); + } catch (NullPointerException ex) { + return; + } + fail(); + } + + @Test + public void testReflectionCompareEx2() { + TestObject o1 = new TestObject(4); + Object o2 = new Object(); + try { + CompareToBuilder.reflectionCompare(o1, o2); + fail(); + } catch (ClassCastException ex) {} + } + + @Test + public void testReflectionHierarchyCompare() { + testReflectionHierarchyCompare(false, null); + } + + @Test + public void testReflectionHierarchyCompareExcludeFields() { + String[] excludeFields = new String[] { "b" }; + testReflectionHierarchyCompare(true, excludeFields); + + TestSubObject x; + TestSubObject y; + TestSubObject z; + + x = new TestSubObject(1, 1); + y = new TestSubObject(2, 1); + z = new TestSubObject(3, 1); + assertXYZCompareOrder(x, y, z, true, excludeFields); + + x = new TestSubObject(1, 3); + y = new TestSubObject(2, 2); + z = new TestSubObject(3, 1); + assertXYZCompareOrder(x, y, z, true, excludeFields); + } + + @Test + public void testReflectionHierarchyCompareTransients() { + testReflectionHierarchyCompare(true, null); + + TestTransientSubObject x; + TestTransientSubObject y; + TestTransientSubObject z; + + x = new TestTransientSubObject(1, 1); + y = new TestTransientSubObject(2, 2); + z = new TestTransientSubObject(3, 3); + assertXYZCompareOrder(x, y, z, true, null); + + x = new TestTransientSubObject(1, 1); + y = new TestTransientSubObject(1, 2); + z = new TestTransientSubObject(1, 3); + assertXYZCompareOrder(x, y, z, true, null); + } + + private void assertXYZCompareOrder(Object x, Object y, Object z, boolean testTransients, String[] excludeFields) { + assertTrue(0 == CompareToBuilder.reflectionCompare(x, x, testTransients, null, excludeFields)); + assertTrue(0 == CompareToBuilder.reflectionCompare(y, y, testTransients, null, excludeFields)); + assertTrue(0 == CompareToBuilder.reflectionCompare(z, z, testTransients, null, excludeFields)); + + assertTrue(0 > CompareToBuilder.reflectionCompare(x, y, testTransients, null, excludeFields)); + assertTrue(0 > CompareToBuilder.reflectionCompare(x, z, testTransients, null, excludeFields)); + assertTrue(0 > CompareToBuilder.reflectionCompare(y, z, testTransients, null, excludeFields)); + + assertTrue(0 < CompareToBuilder.reflectionCompare(y, x, testTransients, null, excludeFields)); + assertTrue(0 < CompareToBuilder.reflectionCompare(z, x, testTransients, null, excludeFields)); + assertTrue(0 < CompareToBuilder.reflectionCompare(z, y, testTransients, null, excludeFields)); + } + + private void testReflectionHierarchyCompare(boolean testTransients, String[] excludeFields) { + TestObject to1 = new TestObject(1); + TestObject to2 = new TestObject(2); + TestObject to3 = new TestObject(3); + TestSubObject tso1 = new TestSubObject(1, 1); + TestSubObject tso2 = new TestSubObject(2, 2); + TestSubObject tso3 = new TestSubObject(3, 3); + + assertReflectionCompareContract(to1, to1, to1, false, excludeFields); + assertReflectionCompareContract(to1, to2, to3, false, excludeFields); + assertReflectionCompareContract(tso1, tso1, tso1, false, excludeFields); + assertReflectionCompareContract(tso1, tso2, tso3, false, excludeFields); + assertReflectionCompareContract("1", "2", "3", false, excludeFields); + + assertTrue(0 != CompareToBuilder.reflectionCompare(tso1, new TestSubObject(1, 0), testTransients)); + assertTrue(0 != CompareToBuilder.reflectionCompare(tso1, new TestSubObject(0, 1), testTransients)); + + // root class + assertXYZCompareOrder(to1, to2, to3, true, null); + // subclass + assertXYZCompareOrder(tso1, tso2, tso3, true, null); + } + + /** + * See "Effective Java" under "Consider Implementing Comparable". + * + * @param x an object to compare + * @param y an object to compare + * @param z an object to compare + * @param testTransients Whether to include transients in the comparison + * @param excludeFields fields to exclude + */ + private void assertReflectionCompareContract(Object x, Object y, Object z, boolean testTransients, String[] excludeFields) { + + // signum + assertTrue(reflectionCompareSignum(x, y, testTransients, excludeFields) == -reflectionCompareSignum(y, x, testTransients, excludeFields)); + + // transitive + if (CompareToBuilder.reflectionCompare(x, y, testTransients, null, excludeFields) > 0 + && CompareToBuilder.reflectionCompare(y, z, testTransients, null, excludeFields) > 0){ + assertTrue(CompareToBuilder.reflectionCompare(x, z, testTransients, null, excludeFields) > 0); + } + + // un-named + if (CompareToBuilder.reflectionCompare(x, y, testTransients, null, excludeFields) == 0) { + assertTrue(reflectionCompareSignum(x, z, testTransients, excludeFields) == -reflectionCompareSignum(y, z, testTransients, excludeFields)); + } + + // strongly recommended but not strictly required + assertTrue(CompareToBuilder.reflectionCompare(x, y, testTransients) ==0 == EqualsBuilder.reflectionEquals(x, y, testTransients)); + } + + /** + * Returns the signum of the result of comparing x and y with + * CompareToBuilder.reflectionCompare + * + * @param lhs The "left-hand-side" of the comparison. + * @param rhs The "right-hand-side" of the comparison. + * @param testTransients Whether to include transients in the comparison + * @param excludeFields fields to exclude + * @return int The signum + */ + private int reflectionCompareSignum(Object lhs, Object rhs, boolean testTransients, String[] excludeFields) { + return BigInteger.valueOf(CompareToBuilder.reflectionCompare(lhs, rhs, testTransients)).signum(); + } + + @Test + public void testAppendSuper() { + TestObject o1 = new TestObject(4); + TestObject o2 = new TestObject(5); + assertTrue(new CompareToBuilder().appendSuper(0).append(o1, o1).toComparison() == 0); + assertTrue(new CompareToBuilder().appendSuper(0).append(o1, o2).toComparison() < 0); + assertTrue(new CompareToBuilder().appendSuper(0).append(o2, o1).toComparison() > 0); + + assertTrue(new CompareToBuilder().appendSuper(-1).append(o1, o1).toComparison() < 0); + assertTrue(new CompareToBuilder().appendSuper(-1).append(o1, o2).toComparison() < 0); + + assertTrue(new CompareToBuilder().appendSuper(1).append(o1, o1).toComparison() > 0); + assertTrue(new CompareToBuilder().appendSuper(1).append(o1, o2).toComparison() > 0); + } + + @Test + public void testObject() { + TestObject o1 = new TestObject(4); + TestObject o2 = new TestObject(4); + assertTrue(new CompareToBuilder().append(o1, o1).toComparison() == 0); + assertTrue(new CompareToBuilder().append(o1, o2).toComparison() == 0); + o2.setA(5); + assertTrue(new CompareToBuilder().append(o1, o2).toComparison() < 0); + assertTrue(new CompareToBuilder().append(o2, o1).toComparison() > 0); + + assertTrue(new CompareToBuilder().append(o1, null).toComparison() > 0); + assertTrue(new CompareToBuilder().append((Object) null, (Object) null).toComparison() == 0); + assertTrue(new CompareToBuilder().append(null, o1).toComparison() < 0); + } + + @Test + public void testObjectBuild() { + TestObject o1 = new TestObject(4); + TestObject o2 = new TestObject(4); + assertEquals(Integer.valueOf(0), new CompareToBuilder().append(o1, o1).build()); + assertEquals(Integer.valueOf(0), new CompareToBuilder().append(o1, o2).build()); + o2.setA(5); + assertTrue(new CompareToBuilder().append(o1, o2).build().intValue() < 0); + assertTrue(new CompareToBuilder().append(o2, o1).build().intValue() > 0); + + assertTrue(new CompareToBuilder().append(o1, null).build().intValue() > 0); + assertEquals(Integer.valueOf(0), new CompareToBuilder().append((Object) null, (Object) null).build()); + assertTrue(new CompareToBuilder().append(null, o1).build().intValue() < 0); + } + + @Test + public void testObjectEx2() { + TestObject o1 = new TestObject(4); + Object o2 = new Object(); + try { + new CompareToBuilder().append(o1, o2); + fail(); + } catch (ClassCastException ex) {} + } + + @Test + public void testObjectComparator() { + String o1 = "Fred"; + String o2 = "Fred"; + assertTrue(new CompareToBuilder().append(o1, o1, String.CASE_INSENSITIVE_ORDER).toComparison() == 0); + assertTrue(new CompareToBuilder().append(o1, o2, String.CASE_INSENSITIVE_ORDER).toComparison() == 0); + o2 = "FRED"; + assertTrue(new CompareToBuilder().append(o1, o2, String.CASE_INSENSITIVE_ORDER).toComparison() == 0); + assertTrue(new CompareToBuilder().append(o2, o1, String.CASE_INSENSITIVE_ORDER).toComparison() == 0); + o2 = "FREDA"; + assertTrue(new CompareToBuilder().append(o1, o2, String.CASE_INSENSITIVE_ORDER).toComparison() < 0); + assertTrue(new CompareToBuilder().append(o2, o1, String.CASE_INSENSITIVE_ORDER).toComparison() > 0); + + assertTrue(new CompareToBuilder().append(o1, null, String.CASE_INSENSITIVE_ORDER).toComparison() > 0); + assertTrue(new CompareToBuilder().append((Object) null, (Object) null, String.CASE_INSENSITIVE_ORDER).toComparison() == 0); + assertTrue(new CompareToBuilder().append(null, o1, String.CASE_INSENSITIVE_ORDER).toComparison() < 0); + } + + @Test + public void testObjectComparatorNull() { + String o1 = "Fred"; + String o2 = "Fred"; + assertTrue(new CompareToBuilder().append(o1, o1, null).toComparison() == 0); + assertTrue(new CompareToBuilder().append(o1, o2, null).toComparison() == 0); + o2 = "Zebra"; + assertTrue(new CompareToBuilder().append(o1, o2, null).toComparison() < 0); + assertTrue(new CompareToBuilder().append(o2, o1, null).toComparison() > 0); + + assertTrue(new CompareToBuilder().append(o1, null, null).toComparison() > 0); + assertTrue(new CompareToBuilder().append((Object) null, (Object) null, null).toComparison() == 0); + assertTrue(new CompareToBuilder().append(null, o1, null).toComparison() < 0); + } + + @Test + public void testLong() { + long o1 = 1L; + long o2 = 2L; + assertTrue(new CompareToBuilder().append(o1, o1).toComparison() == 0); + assertTrue(new CompareToBuilder().append(o1, o2).toComparison() < 0); + assertTrue(new CompareToBuilder().append(o2, o1).toComparison() > 0); + assertTrue(new CompareToBuilder().append(o1, Long.MAX_VALUE).toComparison() < 0); + assertTrue(new CompareToBuilder().append(Long.MAX_VALUE, o1).toComparison() > 0); + assertTrue(new CompareToBuilder().append(o1, Long.MIN_VALUE).toComparison() > 0); + assertTrue(new CompareToBuilder().append(Long.MIN_VALUE, o1).toComparison() < 0); + } + + @Test + public void testInt() { + int o1 = 1; + int o2 = 2; + assertTrue(new CompareToBuilder().append(o1, o1).toComparison() == 0); + assertTrue(new CompareToBuilder().append(o1, o2).toComparison() < 0); + assertTrue(new CompareToBuilder().append(o2, o1).toComparison() > 0); + assertTrue(new CompareToBuilder().append(o1, Integer.MAX_VALUE).toComparison() < 0); + assertTrue(new CompareToBuilder().append(Integer.MAX_VALUE, o1).toComparison() > 0); + assertTrue(new CompareToBuilder().append(o1, Integer.MIN_VALUE).toComparison() > 0); + assertTrue(new CompareToBuilder().append(Integer.MIN_VALUE, o1).toComparison() < 0); + } + + @Test + public void testShort() { + short o1 = 1; + short o2 = 2; + assertTrue(new CompareToBuilder().append(o1, o1).toComparison() == 0); + assertTrue(new CompareToBuilder().append(o1, o2).toComparison() < 0); + assertTrue(new CompareToBuilder().append(o2, o1).toComparison() > 0); + assertTrue(new CompareToBuilder().append(o1, Short.MAX_VALUE).toComparison() < 0); + assertTrue(new CompareToBuilder().append(Short.MAX_VALUE, o1).toComparison() > 0); + assertTrue(new CompareToBuilder().append(o1, Short.MIN_VALUE).toComparison() > 0); + assertTrue(new CompareToBuilder().append(Short.MIN_VALUE, o1).toComparison() < 0); + } + + @Test + public void testChar() { + char o1 = 1; + char o2 = 2; + assertTrue(new CompareToBuilder().append(o1, o1).toComparison() == 0); + assertTrue(new CompareToBuilder().append(o1, o2).toComparison() < 0); + assertTrue(new CompareToBuilder().append(o2, o1).toComparison() > 0); + assertTrue(new CompareToBuilder().append(o1, Character.MAX_VALUE).toComparison() < 0); + assertTrue(new CompareToBuilder().append(Character.MAX_VALUE, o1).toComparison() > 0); + assertTrue(new CompareToBuilder().append(o1, Character.MIN_VALUE).toComparison() > 0); + assertTrue(new CompareToBuilder().append(Character.MIN_VALUE, o1).toComparison() < 0); + } + + @Test + public void testByte() { + byte o1 = 1; + byte o2 = 2; + assertTrue(new CompareToBuilder().append(o1, o1).toComparison() == 0); + assertTrue(new CompareToBuilder().append(o1, o2).toComparison() < 0); + assertTrue(new CompareToBuilder().append(o2, o1).toComparison() > 0); + assertTrue(new CompareToBuilder().append(o1, Byte.MAX_VALUE).toComparison() < 0); + assertTrue(new CompareToBuilder().append(Byte.MAX_VALUE, o1).toComparison() > 0); + assertTrue(new CompareToBuilder().append(o1, Byte.MIN_VALUE).toComparison() > 0); + assertTrue(new CompareToBuilder().append(Byte.MIN_VALUE, o1).toComparison() < 0); + } + + @Test + public void testDouble() { + double o1 = 1; + double o2 = 2; + assertTrue(new CompareToBuilder().append(o1, o1).toComparison() == 0); + assertTrue(new CompareToBuilder().append(o1, o2).toComparison() < 0); + assertTrue(new CompareToBuilder().append(o2, o1).toComparison() > 0); + assertTrue(new CompareToBuilder().append(o1, Double.MAX_VALUE).toComparison() < 0); + assertTrue(new CompareToBuilder().append(Double.MAX_VALUE, o1).toComparison() > 0); + assertTrue(new CompareToBuilder().append(o1, Double.MIN_VALUE).toComparison() > 0); + assertTrue(new CompareToBuilder().append(Double.MIN_VALUE, o1).toComparison() < 0); + assertTrue(new CompareToBuilder().append(Double.NaN, Double.NaN).toComparison() == 0); + assertTrue(new CompareToBuilder().append(Double.NaN, Double.MAX_VALUE).toComparison() > 0); + assertTrue(new CompareToBuilder().append(Double.POSITIVE_INFINITY, Double.MAX_VALUE).toComparison() > 0); + assertTrue(new CompareToBuilder().append(Double.NEGATIVE_INFINITY, Double.MIN_VALUE).toComparison() < 0); + assertTrue(new CompareToBuilder().append(o1, Double.NaN).toComparison() < 0); + assertTrue(new CompareToBuilder().append(Double.NaN, o1).toComparison() > 0); + assertTrue(new CompareToBuilder().append(-0.0, 0.0).toComparison() < 0); + assertTrue(new CompareToBuilder().append(0.0, -0.0).toComparison() > 0); + } + + @Test + public void testFloat() { + float o1 = 1; + float o2 = 2; + assertTrue(new CompareToBuilder().append(o1, o1).toComparison() == 0); + assertTrue(new CompareToBuilder().append(o1, o2).toComparison() < 0); + assertTrue(new CompareToBuilder().append(o2, o1).toComparison() > 0); + assertTrue(new CompareToBuilder().append(o1, Float.MAX_VALUE).toComparison() < 0); + assertTrue(new CompareToBuilder().append(Float.MAX_VALUE, o1).toComparison() > 0); + assertTrue(new CompareToBuilder().append(o1, Float.MIN_VALUE).toComparison() > 0); + assertTrue(new CompareToBuilder().append(Float.MIN_VALUE, o1).toComparison() < 0); + assertTrue(new CompareToBuilder().append(Float.NaN, Float.NaN).toComparison() == 0); + assertTrue(new CompareToBuilder().append(Float.NaN, Float.MAX_VALUE).toComparison() > 0); + assertTrue(new CompareToBuilder().append(Float.POSITIVE_INFINITY, Float.MAX_VALUE).toComparison() > 0); + assertTrue(new CompareToBuilder().append(Float.NEGATIVE_INFINITY, Float.MIN_VALUE).toComparison() < 0); + assertTrue(new CompareToBuilder().append(o1, Float.NaN).toComparison() < 0); + assertTrue(new CompareToBuilder().append(Float.NaN, o1).toComparison() > 0); + assertTrue(new CompareToBuilder().append(-0.0, 0.0).toComparison() < 0); + assertTrue(new CompareToBuilder().append(0.0, -0.0).toComparison() > 0); + } + + @Test + public void testBoolean() { + boolean o1 = true; + boolean o2 = false; + assertTrue(new CompareToBuilder().append(o1, o1).toComparison() == 0); + assertTrue(new CompareToBuilder().append(o2, o2).toComparison() == 0); + assertTrue(new CompareToBuilder().append(o1, o2).toComparison() > 0); + assertTrue(new CompareToBuilder().append(o2, o1).toComparison() < 0); + } + + @Test + public void testObjectArray() { + TestObject[] obj1 = new TestObject[2]; + obj1[0] = new TestObject(4); + obj1[1] = new TestObject(5); + TestObject[] obj2 = new TestObject[2]; + obj2[0] = new TestObject(4); + obj2[1] = new TestObject(5); + TestObject[] obj3 = new TestObject[3]; + obj3[0] = new TestObject(4); + obj3[1] = new TestObject(5); + obj3[2] = new TestObject(6); + + assertTrue(new CompareToBuilder().append(obj1, obj1).toComparison() == 0); + assertTrue(new CompareToBuilder().append(obj1, obj2).toComparison() == 0); + assertTrue(new CompareToBuilder().append(obj1, obj3).toComparison() < 0); + assertTrue(new CompareToBuilder().append(obj3, obj1).toComparison() > 0); + + obj1[1] = new TestObject(7); + assertTrue(new CompareToBuilder().append(obj1, obj2).toComparison() > 0); + assertTrue(new CompareToBuilder().append(obj2, obj1).toComparison() < 0); + + assertTrue(new CompareToBuilder().append(obj1, null).toComparison() > 0); + assertTrue(new CompareToBuilder().append((Object[]) null, (Object[]) null).toComparison() == 0); + assertTrue(new CompareToBuilder().append(null, obj1).toComparison() < 0); + } + + @Test + public void testLongArray() { + long[] obj1 = new long[2]; + obj1[0] = 5L; + obj1[1] = 6L; + long[] obj2 = new long[2]; + obj2[0] = 5L; + obj2[1] = 6L; + long[] obj3 = new long[3]; + obj3[0] = 5L; + obj3[1] = 6L; + obj3[2] = 7L; + + assertTrue(new CompareToBuilder().append(obj1, obj1).toComparison() == 0); + assertTrue(new CompareToBuilder().append(obj1, obj2).toComparison() == 0); + assertTrue(new CompareToBuilder().append(obj1, obj3).toComparison() < 0); + assertTrue(new CompareToBuilder().append(obj3, obj1).toComparison() > 0); + + obj1[1] = 7; + assertTrue(new CompareToBuilder().append(obj1, obj2).toComparison() > 0); + assertTrue(new CompareToBuilder().append(obj2, obj1).toComparison() < 0); + + assertTrue(new CompareToBuilder().append(obj1, null).toComparison() > 0); + assertTrue(new CompareToBuilder().append((long[]) null, (long[]) null).toComparison() == 0); + assertTrue(new CompareToBuilder().append(null, obj1).toComparison() < 0); + } + + @Test + public void testIntArray() { + int[] obj1 = new int[2]; + obj1[0] = 5; + obj1[1] = 6; + int[] obj2 = new int[2]; + obj2[0] = 5; + obj2[1] = 6; + int[] obj3 = new int[3]; + obj3[0] = 5; + obj3[1] = 6; + obj3[2] = 7; + + assertTrue(new CompareToBuilder().append(obj1, obj1).toComparison() == 0); + assertTrue(new CompareToBuilder().append(obj1, obj2).toComparison() == 0); + assertTrue(new CompareToBuilder().append(obj1, obj3).toComparison() < 0); + assertTrue(new CompareToBuilder().append(obj3, obj1).toComparison() > 0); + + obj1[1] = 7; + assertTrue(new CompareToBuilder().append(obj1, obj2).toComparison() > 0); + assertTrue(new CompareToBuilder().append(obj2, obj1).toComparison() < 0); + + assertTrue(new CompareToBuilder().append(obj1, null).toComparison() > 0); + assertTrue(new CompareToBuilder().append((int[]) null, (int[]) null).toComparison() == 0); + assertTrue(new CompareToBuilder().append(null, obj1).toComparison() < 0); + } + + @Test + public void testShortArray() { + short[] obj1 = new short[2]; + obj1[0] = 5; + obj1[1] = 6; + short[] obj2 = new short[2]; + obj2[0] = 5; + obj2[1] = 6; + short[] obj3 = new short[3]; + obj3[0] = 5; + obj3[1] = 6; + obj3[2] = 7; + + assertTrue(new CompareToBuilder().append(obj1, obj1).toComparison() == 0); + assertTrue(new CompareToBuilder().append(obj1, obj2).toComparison() == 0); + assertTrue(new CompareToBuilder().append(obj1, obj3).toComparison() < 0); + assertTrue(new CompareToBuilder().append(obj3, obj1).toComparison() > 0); + + obj1[1] = 7; + assertTrue(new CompareToBuilder().append(obj1, obj2).toComparison() > 0); + assertTrue(new CompareToBuilder().append(obj2, obj1).toComparison() < 0); + + assertTrue(new CompareToBuilder().append(obj1, null).toComparison() > 0); + assertTrue(new CompareToBuilder().append((short[]) null, (short[]) null).toComparison() == 0); + assertTrue(new CompareToBuilder().append(null, obj1).toComparison() < 0); + } + + @Test + public void testCharArray() { + char[] obj1 = new char[2]; + obj1[0] = 5; + obj1[1] = 6; + char[] obj2 = new char[2]; + obj2[0] = 5; + obj2[1] = 6; + char[] obj3 = new char[3]; + obj3[0] = 5; + obj3[1] = 6; + obj3[2] = 7; + + assertTrue(new CompareToBuilder().append(obj1, obj1).toComparison() == 0); + assertTrue(new CompareToBuilder().append(obj1, obj2).toComparison() == 0); + assertTrue(new CompareToBuilder().append(obj1, obj3).toComparison() < 0); + assertTrue(new CompareToBuilder().append(obj3, obj1).toComparison() > 0); + + obj1[1] = 7; + assertTrue(new CompareToBuilder().append(obj1, obj2).toComparison() > 0); + assertTrue(new CompareToBuilder().append(obj2, obj1).toComparison() < 0); + + assertTrue(new CompareToBuilder().append(obj1, null).toComparison() > 0); + assertTrue(new CompareToBuilder().append((char[]) null, (char[]) null).toComparison() == 0); + assertTrue(new CompareToBuilder().append(null, obj1).toComparison() < 0); + } + + @Test + public void testByteArray() { + byte[] obj1 = new byte[2]; + obj1[0] = 5; + obj1[1] = 6; + byte[] obj2 = new byte[2]; + obj2[0] = 5; + obj2[1] = 6; + byte[] obj3 = new byte[3]; + obj3[0] = 5; + obj3[1] = 6; + obj3[2] = 7; + + assertTrue(new CompareToBuilder().append(obj1, obj1).toComparison() == 0); + assertTrue(new CompareToBuilder().append(obj1, obj2).toComparison() == 0); + assertTrue(new CompareToBuilder().append(obj1, obj3).toComparison() < 0); + assertTrue(new CompareToBuilder().append(obj3, obj1).toComparison() > 0); + + obj1[1] = 7; + assertTrue(new CompareToBuilder().append(obj1, obj2).toComparison() > 0); + assertTrue(new CompareToBuilder().append(obj2, obj1).toComparison() < 0); + + assertTrue(new CompareToBuilder().append(obj1, null).toComparison() > 0); + assertTrue(new CompareToBuilder().append((byte[]) null, (byte[]) null).toComparison() == 0); + assertTrue(new CompareToBuilder().append(null, obj1).toComparison() < 0); + } + + @Test + public void testDoubleArray() { + double[] obj1 = new double[2]; + obj1[0] = 5; + obj1[1] = 6; + double[] obj2 = new double[2]; + obj2[0] = 5; + obj2[1] = 6; + double[] obj3 = new double[3]; + obj3[0] = 5; + obj3[1] = 6; + obj3[2] = 7; + + assertTrue(new CompareToBuilder().append(obj1, obj1).toComparison() == 0); + assertTrue(new CompareToBuilder().append(obj1, obj2).toComparison() == 0); + assertTrue(new CompareToBuilder().append(obj1, obj3).toComparison() < 0); + assertTrue(new CompareToBuilder().append(obj3, obj1).toComparison() > 0); + + obj1[1] = 7; + assertTrue(new CompareToBuilder().append(obj1, obj2).toComparison() > 0); + assertTrue(new CompareToBuilder().append(obj2, obj1).toComparison() < 0); + + assertTrue(new CompareToBuilder().append(obj1, null).toComparison() > 0); + assertTrue(new CompareToBuilder().append((double[]) null, (double[]) null).toComparison() == 0); + assertTrue(new CompareToBuilder().append(null, obj1).toComparison() < 0); + } + + @Test + public void testFloatArray() { + float[] obj1 = new float[2]; + obj1[0] = 5; + obj1[1] = 6; + float[] obj2 = new float[2]; + obj2[0] = 5; + obj2[1] = 6; + float[] obj3 = new float[3]; + obj3[0] = 5; + obj3[1] = 6; + obj3[2] = 7; + + assertTrue(new CompareToBuilder().append(obj1, obj1).toComparison() == 0); + assertTrue(new CompareToBuilder().append(obj1, obj2).toComparison() == 0); + assertTrue(new CompareToBuilder().append(obj1, obj3).toComparison() < 0); + assertTrue(new CompareToBuilder().append(obj3, obj1).toComparison() > 0); + + obj1[1] = 7; + assertTrue(new CompareToBuilder().append(obj1, obj2).toComparison() > 0); + assertTrue(new CompareToBuilder().append(obj2, obj1).toComparison() < 0); + + assertTrue(new CompareToBuilder().append(obj1, null).toComparison() > 0); + assertTrue(new CompareToBuilder().append((float[]) null, (float[]) null).toComparison() == 0); + assertTrue(new CompareToBuilder().append(null, obj1).toComparison() < 0); + } + + @Test + public void testBooleanArray() { + boolean[] obj1 = new boolean[2]; + obj1[0] = true; + obj1[1] = false; + boolean[] obj2 = new boolean[2]; + obj2[0] = true; + obj2[1] = false; + boolean[] obj3 = new boolean[3]; + obj3[0] = true; + obj3[1] = false; + obj3[2] = true; + + assertTrue(new CompareToBuilder().append(obj1, obj1).toComparison() == 0); + assertTrue(new CompareToBuilder().append(obj1, obj2).toComparison() == 0); + assertTrue(new CompareToBuilder().append(obj1, obj3).toComparison() < 0); + assertTrue(new CompareToBuilder().append(obj3, obj1).toComparison() > 0); + + obj1[1] = true; + assertTrue(new CompareToBuilder().append(obj1, obj2).toComparison() > 0); + assertTrue(new CompareToBuilder().append(obj2, obj1).toComparison() < 0); + + assertTrue(new CompareToBuilder().append(obj1, null).toComparison() > 0); + assertTrue(new CompareToBuilder().append((boolean[]) null, (boolean[]) null).toComparison() == 0); + assertTrue(new CompareToBuilder().append(null, obj1).toComparison() < 0); + } + + @Test + public void testMultiLongArray() { + long[][] array1 = new long[2][2]; + long[][] array2 = new long[2][2]; + long[][] array3 = new long[2][3]; + for (int i = 0; i < array1.length; ++i) { + for (int j = 0; j < array1[0].length; j++) { + array1[i][j] = (i + 1) * (j + 1); + array2[i][j] = (i + 1) * (j + 1); + array3[i][j] = (i + 1) * (j + 1); + } + } + array3[1][2] = 100; + array3[1][2] = 100; + + assertTrue(new CompareToBuilder().append(array1, array1).toComparison() == 0); + assertTrue(new CompareToBuilder().append(array1, array2).toComparison() == 0); + assertTrue(new CompareToBuilder().append(array1, array3).toComparison() < 0); + assertTrue(new CompareToBuilder().append(array3, array1).toComparison() > 0); + array1[1][1] = 200; + assertTrue(new CompareToBuilder().append(array1, array2).toComparison() > 0); + assertTrue(new CompareToBuilder().append(array2, array1).toComparison() < 0); + } + + @Test + public void testMultiIntArray() { + int[][] array1 = new int[2][2]; + int[][] array2 = new int[2][2]; + int[][] array3 = new int[2][3]; + for (int i = 0; i < array1.length; ++i) { + for (int j = 0; j < array1[0].length; j++) { + array1[i][j] = (i + 1) * (j + 1); + array2[i][j] = (i + 1) * (j + 1); + array3[i][j] = (i + 1) * (j + 1); + } + } + array3[1][2] = 100; + array3[1][2] = 100; + + assertTrue(new CompareToBuilder().append(array1, array1).toComparison() == 0); + assertTrue(new CompareToBuilder().append(array1, array2).toComparison() == 0); + assertTrue(new CompareToBuilder().append(array1, array3).toComparison() < 0); + assertTrue(new CompareToBuilder().append(array3, array1).toComparison() > 0); + array1[1][1] = 200; + assertTrue(new CompareToBuilder().append(array1, array2).toComparison() > 0); + assertTrue(new CompareToBuilder().append(array2, array1).toComparison() < 0); + } + + @Test + public void testMultiShortArray() { + short[][] array1 = new short[2][2]; + short[][] array2 = new short[2][2]; + short[][] array3 = new short[2][3]; + for (short i = 0; i < array1.length; ++i) { + for (short j = 0; j < array1[0].length; j++) { + array1[i][j] = (short)((i + 1) * (j + 1)); + array2[i][j] = (short)((i + 1) * (j + 1)); + array3[i][j] = (short)((i + 1) * (j + 1)); + } + } + array3[1][2] = 100; + array3[1][2] = 100; + + assertTrue(new CompareToBuilder().append(array1, array1).toComparison() == 0); + assertTrue(new CompareToBuilder().append(array1, array2).toComparison() == 0); + assertTrue(new CompareToBuilder().append(array1, array3).toComparison() < 0); + assertTrue(new CompareToBuilder().append(array3, array1).toComparison() > 0); + array1[1][1] = 200; + assertTrue(new CompareToBuilder().append(array1, array2).toComparison() > 0); + assertTrue(new CompareToBuilder().append(array2, array1).toComparison() < 0); + } + + @Test + public void testMultiCharArray() { + char[][] array1 = new char[2][2]; + char[][] array2 = new char[2][2]; + char[][] array3 = new char[2][3]; + for (short i = 0; i < array1.length; ++i) { + for (short j = 0; j < array1[0].length; j++) { + array1[i][j] = (char)((i + 1) * (j + 1)); + array2[i][j] = (char)((i + 1) * (j + 1)); + array3[i][j] = (char)((i + 1) * (j + 1)); + } + } + array3[1][2] = 100; + array3[1][2] = 100; + + assertTrue(new CompareToBuilder().append(array1, array1).toComparison() == 0); + assertTrue(new CompareToBuilder().append(array1, array2).toComparison() == 0); + assertTrue(new CompareToBuilder().append(array1, array3).toComparison() < 0); + assertTrue(new CompareToBuilder().append(array3, array1).toComparison() > 0); + array1[1][1] = 200; + assertTrue(new CompareToBuilder().append(array1, array2).toComparison() > 0); + assertTrue(new CompareToBuilder().append(array2, array1).toComparison() < 0); + } + + @Test + public void testMultiByteArray() { + byte[][] array1 = new byte[2][2]; + byte[][] array2 = new byte[2][2]; + byte[][] array3 = new byte[2][3]; + for (byte i = 0; i < array1.length; ++i) { + for (byte j = 0; j < array1[0].length; j++) { + array1[i][j] = (byte)((i + 1) * (j + 1)); + array2[i][j] = (byte)((i + 1) * (j + 1)); + array3[i][j] = (byte)((i + 1) * (j + 1)); + } + } + array3[1][2] = 100; + array3[1][2] = 100; + + assertTrue(new CompareToBuilder().append(array1, array1).toComparison() == 0); + assertTrue(new CompareToBuilder().append(array1, array2).toComparison() == 0); + assertTrue(new CompareToBuilder().append(array1, array3).toComparison() < 0); + assertTrue(new CompareToBuilder().append(array3, array1).toComparison() > 0); + array1[1][1] = 127; + assertTrue(new CompareToBuilder().append(array1, array2).toComparison() > 0); + assertTrue(new CompareToBuilder().append(array2, array1).toComparison() < 0); + } + + @Test + public void testMultiFloatArray() { + float[][] array1 = new float[2][2]; + float[][] array2 = new float[2][2]; + float[][] array3 = new float[2][3]; + for (int i = 0; i < array1.length; ++i) { + for (int j = 0; j < array1[0].length; j++) { + array1[i][j] = (i + 1) * (j + 1); + array2[i][j] = (i + 1) * (j + 1); + array3[i][j] = (i + 1) * (j + 1); + } + } + array3[1][2] = 100; + array3[1][2] = 100; + + assertTrue(new CompareToBuilder().append(array1, array1).toComparison() == 0); + assertTrue(new CompareToBuilder().append(array1, array2).toComparison() == 0); + assertTrue(new CompareToBuilder().append(array1, array3).toComparison() < 0); + assertTrue(new CompareToBuilder().append(array3, array1).toComparison() > 0); + array1[1][1] = 127; + assertTrue(new CompareToBuilder().append(array1, array2).toComparison() > 0); + assertTrue(new CompareToBuilder().append(array2, array1).toComparison() < 0); + } + + @Test + public void testMultiDoubleArray() { + double[][] array1 = new double[2][2]; + double[][] array2 = new double[2][2]; + double[][] array3 = new double[2][3]; + for (int i = 0; i < array1.length; ++i) { + for (int j = 0; j < array1[0].length; j++) { + array1[i][j] = (i + 1) * (j + 1); + array2[i][j] = (i + 1) * (j + 1); + array3[i][j] = (i + 1) * (j + 1); + } + } + array3[1][2] = 100; + array3[1][2] = 100; + + assertTrue(new CompareToBuilder().append(array1, array1).toComparison() == 0); + assertTrue(new CompareToBuilder().append(array1, array2).toComparison() == 0); + assertTrue(new CompareToBuilder().append(array1, array3).toComparison() < 0); + assertTrue(new CompareToBuilder().append(array3, array1).toComparison() > 0); + array1[1][1] = 127; + assertTrue(new CompareToBuilder().append(array1, array2).toComparison() > 0); + assertTrue(new CompareToBuilder().append(array2, array1).toComparison() < 0); + } + + @Test + public void testMultiBooleanArray() { + boolean[][] array1 = new boolean[2][2]; + boolean[][] array2 = new boolean[2][2]; + boolean[][] array3 = new boolean[2][3]; + for (int i = 0; i < array1.length; ++i) { + for (int j = 0; j < array1[0].length; j++) { + array1[i][j] = i == 1 ^ j == 1; + array2[i][j] = i == 1 ^ j == 1; + array3[i][j] = i == 1 ^ j == 1; + } + } + array3[1][2] = false; + array3[1][2] = false; + + assertTrue(new CompareToBuilder().append(array1, array1).toComparison() == 0); + assertTrue(new CompareToBuilder().append(array1, array2).toComparison() == 0); + assertTrue(new CompareToBuilder().append(array1, array3).toComparison() < 0); + assertTrue(new CompareToBuilder().append(array3, array1).toComparison() > 0); + array1[1][1] = true; + assertTrue(new CompareToBuilder().append(array1, array2).toComparison() > 0); + assertTrue(new CompareToBuilder().append(array2, array1).toComparison() < 0); + } + + @Test + public void testRaggedArray() { + long array1[][] = new long[2][]; + long array2[][] = new long[2][]; + long array3[][] = new long[3][]; + for (int i = 0; i < array1.length; ++i) { + array1[i] = new long[2]; + array2[i] = new long[2]; + array3[i] = new long[3]; + for (int j = 0; j < array1[i].length; ++j) { + array1[i][j] = (i + 1) * (j + 1); + array2[i][j] = (i + 1) * (j + 1); + array3[i][j] = (i + 1) * (j + 1); + } + } + array3[1][2] = 100; + array3[1][2] = 100; + + + assertTrue(new CompareToBuilder().append(array1, array1).toComparison() == 0); + assertTrue(new CompareToBuilder().append(array1, array2).toComparison() == 0); + assertTrue(new CompareToBuilder().append(array1, array3).toComparison() < 0); + assertTrue(new CompareToBuilder().append(array3, array1).toComparison() > 0); + array1[1][1] = 200; + assertTrue(new CompareToBuilder().append(array1, array2).toComparison() > 0); + assertTrue(new CompareToBuilder().append(array2, array1).toComparison() < 0); + } + + @Test + public void testMixedArray() { + Object array1[] = new Object[2]; + Object array2[] = new Object[2]; + Object array3[] = new Object[2]; + for (int i = 0; i < array1.length; ++i) { + array1[i] = new long[2]; + array2[i] = new long[2]; + array3[i] = new long[3]; + for (int j = 0; j < 2; ++j) { + ((long[]) array1[i])[j] = (i + 1) * (j + 1); + ((long[]) array2[i])[j] = (i + 1) * (j + 1); + ((long[]) array3[i])[j] = (i + 1) * (j + 1); + } + } + ((long[]) array3[0])[2] = 1; + ((long[]) array3[1])[2] = 1; + assertTrue(new CompareToBuilder().append(array1, array1).toComparison() == 0); + assertTrue(new CompareToBuilder().append(array1, array2).toComparison() == 0); + assertTrue(new CompareToBuilder().append(array1, array3).toComparison() < 0); + assertTrue(new CompareToBuilder().append(array3, array1).toComparison() > 0); + ((long[]) array1[1])[1] = 200; + assertTrue(new CompareToBuilder().append(array1, array2).toComparison() > 0); + assertTrue(new CompareToBuilder().append(array2, array1).toComparison() < 0); + } + + @Test + public void testObjectArrayHiddenByObject() { + TestObject[] array1 = new TestObject[2]; + array1[0] = new TestObject(4); + array1[1] = new TestObject(5); + TestObject[] array2 = new TestObject[2]; + array2[0] = new TestObject(4); + array2[1] = new TestObject(5); + TestObject[] array3 = new TestObject[3]; + array3[0] = new TestObject(4); + array3[1] = new TestObject(5); + array3[2] = new TestObject(6); + + Object obj1 = array1; + Object obj2 = array2; + Object obj3 = array3; + + assertTrue(new CompareToBuilder().append(obj1, obj1).toComparison() == 0); + assertTrue(new CompareToBuilder().append(obj1, obj2).toComparison() == 0); + assertTrue(new CompareToBuilder().append(obj1, obj3).toComparison() < 0); + assertTrue(new CompareToBuilder().append(obj3, obj1).toComparison() > 0); + + array1[1] = new TestObject(7); + assertTrue(new CompareToBuilder().append(obj1, obj2).toComparison() > 0); + assertTrue(new CompareToBuilder().append(obj2, obj1).toComparison() < 0); + } + + @Test + public void testLongArrayHiddenByObject() { + long[] array1 = new long[2]; + array1[0] = 5L; + array1[1] = 6L; + long[] array2 = new long[2]; + array2[0] = 5L; + array2[1] = 6L; + long[] array3 = new long[3]; + array3[0] = 5L; + array3[1] = 6L; + array3[2] = 7L; + Object obj1 = array1; + Object obj2 = array2; + Object obj3 = array3; + assertTrue(new CompareToBuilder().append(obj1, obj1).toComparison() == 0); + assertTrue(new CompareToBuilder().append(obj1, obj2).toComparison() == 0); + assertTrue(new CompareToBuilder().append(obj1, obj3).toComparison() < 0); + assertTrue(new CompareToBuilder().append(obj3, obj1).toComparison() > 0); + + array1[1] = 7; + assertTrue(new CompareToBuilder().append(obj1, obj2).toComparison() > 0); + assertTrue(new CompareToBuilder().append(obj2, obj1).toComparison() < 0); + } + + @Test + public void testIntArrayHiddenByObject() { + int[] array1 = new int[2]; + array1[0] = 5; + array1[1] = 6; + int[] array2 = new int[2]; + array2[0] = 5; + array2[1] = 6; + int[] array3 = new int[3]; + array3[0] = 5; + array3[1] = 6; + array3[2] = 7; + Object obj1 = array1; + Object obj2 = array2; + Object obj3 = array3; + assertTrue(new CompareToBuilder().append(obj1, obj1).toComparison() == 0); + assertTrue(new CompareToBuilder().append(obj1, obj2).toComparison() == 0); + assertTrue(new CompareToBuilder().append(obj1, obj3).toComparison() < 0); + assertTrue(new CompareToBuilder().append(obj3, obj1).toComparison() > 0); + + array1[1] = 7; + assertTrue(new CompareToBuilder().append(obj1, obj2).toComparison() > 0); + assertTrue(new CompareToBuilder().append(obj2, obj1).toComparison() < 0); + } + + @Test + public void testShortArrayHiddenByObject() { + short[] array1 = new short[2]; + array1[0] = 5; + array1[1] = 6; + short[] array2 = new short[2]; + array2[0] = 5; + array2[1] = 6; + short[] array3 = new short[3]; + array3[0] = 5; + array3[1] = 6; + array3[2] = 7; + Object obj1 = array1; + Object obj2 = array2; + Object obj3 = array3; + assertTrue(new CompareToBuilder().append(obj1, obj1).toComparison() == 0); + assertTrue(new CompareToBuilder().append(obj1, obj2).toComparison() == 0); + assertTrue(new CompareToBuilder().append(obj1, obj3).toComparison() < 0); + assertTrue(new CompareToBuilder().append(obj3, obj1).toComparison() > 0); + + array1[1] = 7; + assertTrue(new CompareToBuilder().append(obj1, obj2).toComparison() > 0); + assertTrue(new CompareToBuilder().append(obj2, obj1).toComparison() < 0); + } + + @Test + public void testCharArrayHiddenByObject() { + char[] array1 = new char[2]; + array1[0] = 5; + array1[1] = 6; + char[] array2 = new char[2]; + array2[0] = 5; + array2[1] = 6; + char[] array3 = new char[3]; + array3[0] = 5; + array3[1] = 6; + array3[2] = 7; + Object obj1 = array1; + Object obj2 = array2; + Object obj3 = array3; + assertTrue(new CompareToBuilder().append(obj1, obj1).toComparison() == 0); + assertTrue(new CompareToBuilder().append(obj1, obj2).toComparison() == 0); + assertTrue(new CompareToBuilder().append(obj1, obj3).toComparison() < 0); + assertTrue(new CompareToBuilder().append(obj3, obj1).toComparison() > 0); + + array1[1] = 7; + assertTrue(new CompareToBuilder().append(obj1, obj2).toComparison() > 0); + assertTrue(new CompareToBuilder().append(obj2, obj1).toComparison() < 0); + } + + @Test + public void testByteArrayHiddenByObject() { + byte[] array1 = new byte[2]; + array1[0] = 5; + array1[1] = 6; + byte[] array2 = new byte[2]; + array2[0] = 5; + array2[1] = 6; + byte[] array3 = new byte[3]; + array3[0] = 5; + array3[1] = 6; + array3[2] = 7; + Object obj1 = array1; + Object obj2 = array2; + Object obj3 = array3; + assertTrue(new CompareToBuilder().append(obj1, obj1).toComparison() == 0); + assertTrue(new CompareToBuilder().append(obj1, obj2).toComparison() == 0); + assertTrue(new CompareToBuilder().append(obj1, obj3).toComparison() < 0); + assertTrue(new CompareToBuilder().append(obj3, obj1).toComparison() > 0); + + array1[1] = 7; + assertTrue(new CompareToBuilder().append(obj1, obj2).toComparison() > 0); + assertTrue(new CompareToBuilder().append(obj2, obj1).toComparison() < 0); + } + + @Test + public void testDoubleArrayHiddenByObject() { + double[] array1 = new double[2]; + array1[0] = 5; + array1[1] = 6; + double[] array2 = new double[2]; + array2[0] = 5; + array2[1] = 6; + double[] array3 = new double[3]; + array3[0] = 5; + array3[1] = 6; + array3[2] = 7; + Object obj1 = array1; + Object obj2 = array2; + Object obj3 = array3; + assertTrue(new CompareToBuilder().append(obj1, obj1).toComparison() == 0); + assertTrue(new CompareToBuilder().append(obj1, obj2).toComparison() == 0); + assertTrue(new CompareToBuilder().append(obj1, obj3).toComparison() < 0); + assertTrue(new CompareToBuilder().append(obj3, obj1).toComparison() > 0); + + array1[1] = 7; + assertTrue(new CompareToBuilder().append(obj1, obj2).toComparison() > 0); + assertTrue(new CompareToBuilder().append(obj2, obj1).toComparison() < 0); + } + + @Test + public void testFloatArrayHiddenByObject() { + float[] array1 = new float[2]; + array1[0] = 5; + array1[1] = 6; + float[] array2 = new float[2]; + array2[0] = 5; + array2[1] = 6; + float[] array3 = new float[3]; + array3[0] = 5; + array3[1] = 6; + array3[2] = 7; + Object obj1 = array1; + Object obj2 = array2; + Object obj3 = array3; + assertTrue(new CompareToBuilder().append(obj1, obj1).toComparison() == 0); + assertTrue(new CompareToBuilder().append(obj1, obj2).toComparison() == 0); + assertTrue(new CompareToBuilder().append(obj1, obj3).toComparison() < 0); + assertTrue(new CompareToBuilder().append(obj3, obj1).toComparison() > 0); + + array1[1] = 7; + assertTrue(new CompareToBuilder().append(obj1, obj2).toComparison() > 0); + assertTrue(new CompareToBuilder().append(obj2, obj1).toComparison() < 0); + } + + @Test + public void testBooleanArrayHiddenByObject() { + boolean[] array1 = new boolean[2]; + array1[0] = true; + array1[1] = false; + boolean[] array2 = new boolean[2]; + array2[0] = true; + array2[1] = false; + boolean[] array3 = new boolean[3]; + array3[0] = true; + array3[1] = false; + array3[2] = true; + Object obj1 = array1; + Object obj2 = array2; + Object obj3 = array3; + assertTrue(new CompareToBuilder().append(obj1, obj1).toComparison() == 0); + assertTrue(new CompareToBuilder().append(obj1, obj2).toComparison() == 0); + assertTrue(new CompareToBuilder().append(obj1, obj3).toComparison() < 0); + assertTrue(new CompareToBuilder().append(obj3, obj1).toComparison() > 0); + + array1[1] = true; + assertTrue(new CompareToBuilder().append(obj1, obj2).toComparison() > 0); + assertTrue(new CompareToBuilder().append(obj2, obj1).toComparison() < 0); + } + + } diff --git a/ApacheCommonsLang/org/apache/commons/lang3/builder/DefaultToStringStyleTest.java b/ApacheCommonsLang/org/apache/commons/lang3/builder/DefaultToStringStyleTest.java new file mode 100644 index 0000000..4d3c71d --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/builder/DefaultToStringStyleTest.java @@ -0,0 +1,133 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.builder; + +import static org.junit.Assert.assertEquals; + +import java.util.ArrayList; +import java.util.HashMap; + +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; +import org.apache.commons.lang3.builder.ToStringStyleTest.Person; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +/** + * Unit tests {@link org.apache.commons.lang3.builder.DefaultToStringStyleTest}. + * + * @version $Id: DefaultToStringStyleTest.java 1185710 2011-10-18 15:07:07Z ggregory $ + */ +public class DefaultToStringStyleTest { + + private final Integer base = Integer.valueOf(5); + private final String baseStr = base.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(base)); + + @Before + public void setUp() throws Exception { + ToStringBuilder.setDefaultStyle(ToStringStyle.DEFAULT_STYLE); + } + + @After + public void tearDown() throws Exception { + ToStringBuilder.setDefaultStyle(ToStringStyle.DEFAULT_STYLE); + } + + //---------------------------------------------------------------- + + @Test + public void testBlank() { + assertEquals(baseStr + "[]", new ToStringBuilder(base).toString()); + } + + @Test + public void testAppendSuper() { + assertEquals(baseStr + "[]", new ToStringBuilder(base).appendSuper("Integer@8888[]").toString()); + assertEquals(baseStr + "[]", new ToStringBuilder(base).appendSuper("Integer@8888[]").toString()); + + assertEquals(baseStr + "[a=hello]", new ToStringBuilder(base).appendSuper("Integer@8888[]").append("a", "hello").toString()); + assertEquals(baseStr + "[,a=hello]", new ToStringBuilder(base).appendSuper("Integer@8888[]").append("a", "hello").toString()); + assertEquals(baseStr + "[a=hello]", new ToStringBuilder(base).appendSuper(null).append("a", "hello").toString()); + } + + @Test + public void testObject() { + Integer i3 = Integer.valueOf(3); + Integer i4 = Integer.valueOf(4); + assertEquals(baseStr + "[]", new ToStringBuilder(base).append((Object) null).toString()); + assertEquals(baseStr + "[3]", new ToStringBuilder(base).append(i3).toString()); + assertEquals(baseStr + "[a=]", new ToStringBuilder(base).append("a", (Object) null).toString()); + assertEquals(baseStr + "[a=3]", new ToStringBuilder(base).append("a", i3).toString()); + assertEquals(baseStr + "[a=3,b=4]", new ToStringBuilder(base).append("a", i3).append("b", i4).toString()); + assertEquals(baseStr + "[a=]", new ToStringBuilder(base).append("a", i3, false).toString()); + assertEquals(baseStr + "[a=]", new ToStringBuilder(base).append("a", new ArrayList(), false).toString()); + assertEquals(baseStr + "[a=[]]", new ToStringBuilder(base).append("a", new ArrayList(), true).toString()); + assertEquals(baseStr + "[a=]", new ToStringBuilder(base).append("a", new HashMap(), false).toString()); + assertEquals(baseStr + "[a={}]", new ToStringBuilder(base).append("a", new HashMap(), true).toString()); + assertEquals(baseStr + "[a=]", new ToStringBuilder(base).append("a", (Object) new String[0], false).toString()); + assertEquals(baseStr + "[a={}]", new ToStringBuilder(base).append("a", (Object) new String[0], true).toString()); + } + + @Test + public void testPerson() { + Person p = new Person(); + p.name = "John Doe"; + p.age = 33; + p.smoker = false; + String pBaseStr = p.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(p)); + assertEquals(pBaseStr + "[name=John Doe,age=33,smoker=false]", new ToStringBuilder(p).append("name", p.name).append("age", p.age).append("smoker", p.smoker).toString()); + } + + @Test + public void testLong() { + assertEquals(baseStr + "[3]", new ToStringBuilder(base).append(3L).toString()); + assertEquals(baseStr + "[a=3]", new ToStringBuilder(base).append("a", 3L).toString()); + assertEquals(baseStr + "[a=3,b=4]", new ToStringBuilder(base).append("a", 3L).append("b", 4L).toString()); + } + + @Test + public void testObjectArray() { + Object[] array = new Object[] {null, base, new int[] {3, 6}}; + assertEquals(baseStr + "[{,5,{3,6}}]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[{,5,{3,6}}]", new ToStringBuilder(base).append((Object) array).toString()); + array = null; + assertEquals(baseStr + "[]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[]", new ToStringBuilder(base).append((Object) array).toString()); + } + + @Test + public void testLongArray() { + long[] array = new long[] {1, 2, -3, 4}; + assertEquals(baseStr + "[{1,2,-3,4}]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[{1,2,-3,4}]", new ToStringBuilder(base).append((Object) array).toString()); + array = null; + assertEquals(baseStr + "[]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[]", new ToStringBuilder(base).append((Object) array).toString()); + } + + @Test + public void testLongArrayArray() { + long[][] array = new long[][] {{1, 2}, null, {5}}; + assertEquals(baseStr + "[{{1,2},,{5}}]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[{{1,2},,{5}}]", new ToStringBuilder(base).append((Object) array).toString()); + array = null; + assertEquals(baseStr + "[]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[]", new ToStringBuilder(base).append((Object) array).toString()); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/builder/EqualsBuilder.java b/ApacheCommonsLang/org/apache/commons/lang3/builder/EqualsBuilder.java new file mode 100644 index 0000000..a8a889f --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/builder/EqualsBuilder.java @@ -0,0 +1,945 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.builder; + +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.tuple.Pair; + +/** + *

Assists in implementing {@link Object#equals(Object)} methods.

+ * + *

This class provides methods to build a good equals method for any + * class. It follows rules laid out in + * Effective Java + * , by Joshua Bloch. In particular the rule for comparing doubles, + * floats, and arrays can be tricky. Also, making sure that + * equals() and hashCode() are consistent can be + * difficult.

+ * + *

Two Objects that compare as equals must generate the same hash code, + * but two Objects with the same hash code do not have to be equal.

+ * + *

All relevant fields should be included in the calculation of equals. + * Derived fields may be ignored. In particular, any field used in + * generating a hash code must be used in the equals method, and vice + * versa.

+ * + *

Typical use for the code is as follows:

+ *
+ * public boolean equals(Object obj) {
+ *   if (obj == null) { return false; }
+ *   if (obj == this) { return true; }
+ *   if (obj.getClass() != getClass()) {
+ *     return false;
+ *   }
+ *   MyClass rhs = (MyClass) obj;
+ *   return new EqualsBuilder()
+ *                 .appendSuper(super.equals(obj))
+ *                 .append(field1, rhs.field1)
+ *                 .append(field2, rhs.field2)
+ *                 .append(field3, rhs.field3)
+ *                 .isEquals();
+ *  }
+ * 
+ * + *

Alternatively, there is a method that uses reflection to determine + * the fields to test. Because these fields are usually private, the method, + * reflectionEquals, uses AccessibleObject.setAccessible to + * change the visibility of the fields. This will fail under a security + * manager, unless the appropriate permissions are set up correctly. It is + * also slower than testing explicitly.

+ * + *

A typical invocation for this method would look like:

+ *
+ * public boolean equals(Object obj) {
+ *   return EqualsBuilder.reflectionEquals(this, obj);
+ * }
+ * 
+ * + * @since 1.0 + * @version $Id: EqualsBuilder.java 1309977 2012-04-05 17:53:39Z ggregory $ + */ +public class EqualsBuilder implements Builder { + + /** + *

+ * A registry of objects used by reflection methods to detect cyclical object references and avoid infinite loops. + *

+ * + * @since 3.0 + */ + private static final ThreadLocal>> REGISTRY = new ThreadLocal>>(); + + /* + * NOTE: we cannot store the actual objects in a HashSet, as that would use the very hashCode() + * we are in the process of calculating. + * + * So we generate a one-to-one mapping from the original object to a new object. + * + * Now HashSet uses equals() to determine if two elements with the same hashcode really + * are equal, so we also need to ensure that the replacement objects are only equal + * if the original objects are identical. + * + * The original implementation (2.4 and before) used the System.indentityHashCode() + * method - however this is not guaranteed to generate unique ids (e.g. LANG-459) + * + * We now use the IDKey helper class (adapted from org.apache.axis.utils.IDKey) + * to disambiguate the duplicate ids. + */ + + /** + *

+ * Returns the registry of object pairs being traversed by the reflection + * methods in the current thread. + *

+ * + * @return Set the registry of objects being traversed + * @since 3.0 + */ + static Set> getRegistry() { + return REGISTRY.get(); + } + + /** + *

+ * Converters value pair into a register pair. + *

+ * + * @param lhs this object + * @param rhs the other object + * + * @return the pair + */ + static Pair getRegisterPair(Object lhs, Object rhs) { + IDKey left = new IDKey(lhs); + IDKey right = new IDKey(rhs); + return Pair.of(left, right); + } + + /** + *

+ * Returns true if the registry contains the given object pair. + * Used by the reflection methods to avoid infinite loops. + * Objects might be swapped therefore a check is needed if the object pair + * is registered in given or swapped order. + *

+ * + * @param lhs this object to lookup in registry + * @param rhs the other object to lookup on registry + * @return boolean true if the registry contains the given object. + * @since 3.0 + */ + static boolean isRegistered(Object lhs, Object rhs) { + Set> registry = getRegistry(); + Pair pair = getRegisterPair(lhs, rhs); + Pair swappedPair = Pair.of(pair.getLeft(), pair.getRight()); + + return registry != null + && (registry.contains(pair) || registry.contains(swappedPair)); + } + + /** + *

+ * Registers the given object pair. + * Used by the reflection methods to avoid infinite loops. + *

+ * + * @param lhs this object to register + * @param rhs the other object to register + */ + static void register(Object lhs, Object rhs) { + synchronized (EqualsBuilder.class) { + if (getRegistry() == null) { + REGISTRY.set(new HashSet>()); + } + } + + Set> registry = getRegistry(); + Pair pair = getRegisterPair(lhs, rhs); + registry.add(pair); + } + + /** + *

+ * Unregisters the given object pair. + *

+ * + *

+ * Used by the reflection methods to avoid infinite loops. + * + * @param lhs this object to unregister + * @param rhs the other object to unregister + * @since 3.0 + */ + static void unregister(Object lhs, Object rhs) { + Set> registry = getRegistry(); + if (registry != null) { + Pair pair = getRegisterPair(lhs, rhs); + registry.remove(pair); + synchronized (EqualsBuilder.class) { + //read again + registry = getRegistry(); + if (registry != null && registry.isEmpty()) { + REGISTRY.remove(); + } + } + } + } + + /** + * If the fields tested are equals. + * The default value is true. + */ + private boolean isEquals = true; + + /** + *

Constructor for EqualsBuilder.

+ * + *

Starts off assuming that equals is true.

+ * @see Object#equals(Object) + */ + public EqualsBuilder() { + // do nothing for now. + } + + //------------------------------------------------------------------------- + + /** + *

This method uses reflection to determine if the two Objects + * are equal.

+ * + *

It uses AccessibleObject.setAccessible to gain access to private + * fields. This means that it will throw a security exception if run under + * a security manager, if the permissions are not set up correctly. It is also + * not as efficient as testing explicitly.

+ * + *

Transient members will be not be tested, as they are likely derived + * fields, and not part of the value of the Object.

+ * + *

Static fields will not be tested. Superclass fields will be included.

+ * + * @param lhs this object + * @param rhs the other object + * @param excludeFields Collection of String field names to exclude from testing + * @return true if the two Objects have tested equals. + */ + public static boolean reflectionEquals(Object lhs, Object rhs, Collection excludeFields) { + return reflectionEquals(lhs, rhs, ReflectionToStringBuilder.toNoNullStringArray(excludeFields)); + } + + /** + *

This method uses reflection to determine if the two Objects + * are equal.

+ * + *

It uses AccessibleObject.setAccessible to gain access to private + * fields. This means that it will throw a security exception if run under + * a security manager, if the permissions are not set up correctly. It is also + * not as efficient as testing explicitly.

+ * + *

Transient members will be not be tested, as they are likely derived + * fields, and not part of the value of the Object.

+ * + *

Static fields will not be tested. Superclass fields will be included.

+ * + * @param lhs this object + * @param rhs the other object + * @param excludeFields array of field names to exclude from testing + * @return true if the two Objects have tested equals. + */ + public static boolean reflectionEquals(Object lhs, Object rhs, String... excludeFields) { + return reflectionEquals(lhs, rhs, false, null, excludeFields); + } + + /** + *

This method uses reflection to determine if the two Objects + * are equal.

+ * + *

It uses AccessibleObject.setAccessible to gain access to private + * fields. This means that it will throw a security exception if run under + * a security manager, if the permissions are not set up correctly. It is also + * not as efficient as testing explicitly.

+ * + *

If the TestTransients parameter is set to true, transient + * members will be tested, otherwise they are ignored, as they are likely + * derived fields, and not part of the value of the Object.

+ * + *

Static fields will not be tested. Superclass fields will be included.

+ * + * @param lhs this object + * @param rhs the other object + * @param testTransients whether to include transient fields + * @return true if the two Objects have tested equals. + */ + public static boolean reflectionEquals(Object lhs, Object rhs, boolean testTransients) { + return reflectionEquals(lhs, rhs, testTransients, null); + } + + /** + *

This method uses reflection to determine if the two Objects + * are equal.

+ * + *

It uses AccessibleObject.setAccessible to gain access to private + * fields. This means that it will throw a security exception if run under + * a security manager, if the permissions are not set up correctly. It is also + * not as efficient as testing explicitly.

+ * + *

If the testTransients parameter is set to true, transient + * members will be tested, otherwise they are ignored, as they are likely + * derived fields, and not part of the value of the Object.

+ * + *

Static fields will not be included. Superclass fields will be appended + * up to and including the specified superclass. A null superclass is treated + * as java.lang.Object.

+ * + * @param lhs this object + * @param rhs the other object + * @param testTransients whether to include transient fields + * @param reflectUpToClass the superclass to reflect up to (inclusive), + * may be null + * @param excludeFields array of field names to exclude from testing + * @return true if the two Objects have tested equals. + * @since 2.0 + */ + public static boolean reflectionEquals(Object lhs, Object rhs, boolean testTransients, Class reflectUpToClass, + String... excludeFields) { + if (lhs == rhs) { + return true; + } + if (lhs == null || rhs == null) { + return false; + } + // Find the leaf class since there may be transients in the leaf + // class or in classes between the leaf and root. + // If we are not testing transients or a subclass has no ivars, + // then a subclass can test equals to a superclass. + Class lhsClass = lhs.getClass(); + Class rhsClass = rhs.getClass(); + Class testClass; + if (lhsClass.isInstance(rhs)) { + testClass = lhsClass; + if (!rhsClass.isInstance(lhs)) { + // rhsClass is a subclass of lhsClass + testClass = rhsClass; + } + } else if (rhsClass.isInstance(lhs)) { + testClass = rhsClass; + if (!lhsClass.isInstance(rhs)) { + // lhsClass is a subclass of rhsClass + testClass = lhsClass; + } + } else { + // The two classes are not related. + return false; + } + EqualsBuilder equalsBuilder = new EqualsBuilder(); + try { + reflectionAppend(lhs, rhs, testClass, equalsBuilder, testTransients, excludeFields); + while (testClass.getSuperclass() != null && testClass != reflectUpToClass) { + testClass = testClass.getSuperclass(); + reflectionAppend(lhs, rhs, testClass, equalsBuilder, testTransients, excludeFields); + } + } catch (IllegalArgumentException e) { + // In this case, we tried to test a subclass vs. a superclass and + // the subclass has ivars or the ivars are transient and + // we are testing transients. + // If a subclass has ivars that we are trying to test them, we get an + // exception and we know that the objects are not equal. + return false; + } + return equalsBuilder.isEquals(); + } + + /** + *

Appends the fields and values defined by the given object of the + * given Class.

+ * + * @param lhs the left hand object + * @param rhs the right hand object + * @param clazz the class to append details of + * @param builder the builder to append to + * @param useTransients whether to test transient fields + * @param excludeFields array of field names to exclude from testing + */ + private static void reflectionAppend( + Object lhs, + Object rhs, + Class clazz, + EqualsBuilder builder, + boolean useTransients, + String[] excludeFields) { + + if (isRegistered(lhs, rhs)) { + return; + } + + try { + register(lhs, rhs); + Field[] fields = clazz.getDeclaredFields(); + AccessibleObject.setAccessible(fields, true); + for (int i = 0; i < fields.length && builder.isEquals; i++) { + Field f = fields[i]; + if (!ArrayUtils.contains(excludeFields, f.getName()) + && (f.getName().indexOf('$') == -1) + && (useTransients || !Modifier.isTransient(f.getModifiers())) + && (!Modifier.isStatic(f.getModifiers()))) { + try { + builder.append(f.get(lhs), f.get(rhs)); + } catch (IllegalAccessException e) { + //this can't happen. Would get a Security exception instead + //throw a runtime exception in case the impossible happens. + throw new InternalError("Unexpected IllegalAccessException"); + } + } + } + } finally { + unregister(lhs, rhs); + } + } + + //------------------------------------------------------------------------- + + /** + *

Adds the result of super.equals() to this builder.

+ * + * @param superEquals the result of calling super.equals() + * @return EqualsBuilder - used to chain calls. + * @since 2.0 + */ + public EqualsBuilder appendSuper(boolean superEquals) { + if (isEquals == false) { + return this; + } + isEquals = superEquals; + return this; + } + + //------------------------------------------------------------------------- + + /** + *

Test if two Objects are equal using their + * equals method.

+ * + * @param lhs the left hand object + * @param rhs the right hand object + * @return EqualsBuilder - used to chain calls. + */ + public EqualsBuilder append(Object lhs, Object rhs) { + if (isEquals == false) { + return this; + } + if (lhs == rhs) { + return this; + } + if (lhs == null || rhs == null) { + this.setEquals(false); + return this; + } + Class lhsClass = lhs.getClass(); + if (!lhsClass.isArray()) { + // The simple case, not an array, just test the element + isEquals = lhs.equals(rhs); + } else if (lhs.getClass() != rhs.getClass()) { + // Here when we compare different dimensions, for example: a boolean[][] to a boolean[] + this.setEquals(false); + } + // 'Switch' on type of array, to dispatch to the correct handler + // This handles multi dimensional arrays of the same depth + else if (lhs instanceof long[]) { + append((long[]) lhs, (long[]) rhs); + } else if (lhs instanceof int[]) { + append((int[]) lhs, (int[]) rhs); + } else if (lhs instanceof short[]) { + append((short[]) lhs, (short[]) rhs); + } else if (lhs instanceof char[]) { + append((char[]) lhs, (char[]) rhs); + } else if (lhs instanceof byte[]) { + append((byte[]) lhs, (byte[]) rhs); + } else if (lhs instanceof double[]) { + append((double[]) lhs, (double[]) rhs); + } else if (lhs instanceof float[]) { + append((float[]) lhs, (float[]) rhs); + } else if (lhs instanceof boolean[]) { + append((boolean[]) lhs, (boolean[]) rhs); + } else { + // Not an array of primitives + append((Object[]) lhs, (Object[]) rhs); + } + return this; + } + + /** + *

+ * Test if two long s are equal. + *

+ * + * @param lhs + * the left hand long + * @param rhs + * the right hand long + * @return EqualsBuilder - used to chain calls. + */ + public EqualsBuilder append(long lhs, long rhs) { + if (isEquals == false) { + return this; + } + isEquals = (lhs == rhs); + return this; + } + + /** + *

Test if two ints are equal.

+ * + * @param lhs the left hand int + * @param rhs the right hand int + * @return EqualsBuilder - used to chain calls. + */ + public EqualsBuilder append(int lhs, int rhs) { + if (isEquals == false) { + return this; + } + isEquals = (lhs == rhs); + return this; + } + + /** + *

Test if two shorts are equal.

+ * + * @param lhs the left hand short + * @param rhs the right hand short + * @return EqualsBuilder - used to chain calls. + */ + public EqualsBuilder append(short lhs, short rhs) { + if (isEquals == false) { + return this; + } + isEquals = (lhs == rhs); + return this; + } + + /** + *

Test if two chars are equal.

+ * + * @param lhs the left hand char + * @param rhs the right hand char + * @return EqualsBuilder - used to chain calls. + */ + public EqualsBuilder append(char lhs, char rhs) { + if (isEquals == false) { + return this; + } + isEquals = (lhs == rhs); + return this; + } + + /** + *

Test if two bytes are equal.

+ * + * @param lhs the left hand byte + * @param rhs the right hand byte + * @return EqualsBuilder - used to chain calls. + */ + public EqualsBuilder append(byte lhs, byte rhs) { + if (isEquals == false) { + return this; + } + isEquals = (lhs == rhs); + return this; + } + + /** + *

Test if two doubles are equal by testing that the + * pattern of bits returned by doubleToLong are equal.

+ * + *

This handles NaNs, Infinities, and -0.0.

+ * + *

It is compatible with the hash code generated by + * HashCodeBuilder.

+ * + * @param lhs the left hand double + * @param rhs the right hand double + * @return EqualsBuilder - used to chain calls. + */ + public EqualsBuilder append(double lhs, double rhs) { + if (isEquals == false) { + return this; + } + return append(Double.doubleToLongBits(lhs), Double.doubleToLongBits(rhs)); + } + + /** + *

Test if two floats are equal byt testing that the + * pattern of bits returned by doubleToLong are equal.

+ * + *

This handles NaNs, Infinities, and -0.0.

+ * + *

It is compatible with the hash code generated by + * HashCodeBuilder.

+ * + * @param lhs the left hand float + * @param rhs the right hand float + * @return EqualsBuilder - used to chain calls. + */ + public EqualsBuilder append(float lhs, float rhs) { + if (isEquals == false) { + return this; + } + return append(Float.floatToIntBits(lhs), Float.floatToIntBits(rhs)); + } + + /** + *

Test if two booleanss are equal.

+ * + * @param lhs the left hand boolean + * @param rhs the right hand boolean + * @return EqualsBuilder - used to chain calls. + */ + public EqualsBuilder append(boolean lhs, boolean rhs) { + if (isEquals == false) { + return this; + } + isEquals = (lhs == rhs); + return this; + } + + /** + *

Performs a deep comparison of two Object arrays.

+ * + *

This also will be called for the top level of + * multi-dimensional, ragged, and multi-typed arrays.

+ * + * @param lhs the left hand Object[] + * @param rhs the right hand Object[] + * @return EqualsBuilder - used to chain calls. + */ + public EqualsBuilder append(Object[] lhs, Object[] rhs) { + if (isEquals == false) { + return this; + } + if (lhs == rhs) { + return this; + } + if (lhs == null || rhs == null) { + this.setEquals(false); + return this; + } + if (lhs.length != rhs.length) { + this.setEquals(false); + return this; + } + for (int i = 0; i < lhs.length && isEquals; ++i) { + append(lhs[i], rhs[i]); + } + return this; + } + + /** + *

Deep comparison of array of long. Length and all + * values are compared.

+ * + *

The method {@link #append(long, long)} is used.

+ * + * @param lhs the left hand long[] + * @param rhs the right hand long[] + * @return EqualsBuilder - used to chain calls. + */ + public EqualsBuilder append(long[] lhs, long[] rhs) { + if (isEquals == false) { + return this; + } + if (lhs == rhs) { + return this; + } + if (lhs == null || rhs == null) { + this.setEquals(false); + return this; + } + if (lhs.length != rhs.length) { + this.setEquals(false); + return this; + } + for (int i = 0; i < lhs.length && isEquals; ++i) { + append(lhs[i], rhs[i]); + } + return this; + } + + /** + *

Deep comparison of array of int. Length and all + * values are compared.

+ * + *

The method {@link #append(int, int)} is used.

+ * + * @param lhs the left hand int[] + * @param rhs the right hand int[] + * @return EqualsBuilder - used to chain calls. + */ + public EqualsBuilder append(int[] lhs, int[] rhs) { + if (isEquals == false) { + return this; + } + if (lhs == rhs) { + return this; + } + if (lhs == null || rhs == null) { + this.setEquals(false); + return this; + } + if (lhs.length != rhs.length) { + this.setEquals(false); + return this; + } + for (int i = 0; i < lhs.length && isEquals; ++i) { + append(lhs[i], rhs[i]); + } + return this; + } + + /** + *

Deep comparison of array of short. Length and all + * values are compared.

+ * + *

The method {@link #append(short, short)} is used.

+ * + * @param lhs the left hand short[] + * @param rhs the right hand short[] + * @return EqualsBuilder - used to chain calls. + */ + public EqualsBuilder append(short[] lhs, short[] rhs) { + if (isEquals == false) { + return this; + } + if (lhs == rhs) { + return this; + } + if (lhs == null || rhs == null) { + this.setEquals(false); + return this; + } + if (lhs.length != rhs.length) { + this.setEquals(false); + return this; + } + for (int i = 0; i < lhs.length && isEquals; ++i) { + append(lhs[i], rhs[i]); + } + return this; + } + + /** + *

Deep comparison of array of char. Length and all + * values are compared.

+ * + *

The method {@link #append(char, char)} is used.

+ * + * @param lhs the left hand char[] + * @param rhs the right hand char[] + * @return EqualsBuilder - used to chain calls. + */ + public EqualsBuilder append(char[] lhs, char[] rhs) { + if (isEquals == false) { + return this; + } + if (lhs == rhs) { + return this; + } + if (lhs == null || rhs == null) { + this.setEquals(false); + return this; + } + if (lhs.length != rhs.length) { + this.setEquals(false); + return this; + } + for (int i = 0; i < lhs.length && isEquals; ++i) { + append(lhs[i], rhs[i]); + } + return this; + } + + /** + *

Deep comparison of array of byte. Length and all + * values are compared.

+ * + *

The method {@link #append(byte, byte)} is used.

+ * + * @param lhs the left hand byte[] + * @param rhs the right hand byte[] + * @return EqualsBuilder - used to chain calls. + */ + public EqualsBuilder append(byte[] lhs, byte[] rhs) { + if (isEquals == false) { + return this; + } + if (lhs == rhs) { + return this; + } + if (lhs == null || rhs == null) { + this.setEquals(false); + return this; + } + if (lhs.length != rhs.length) { + this.setEquals(false); + return this; + } + for (int i = 0; i < lhs.length && isEquals; ++i) { + append(lhs[i], rhs[i]); + } + return this; + } + + /** + *

Deep comparison of array of double. Length and all + * values are compared.

+ * + *

The method {@link #append(double, double)} is used.

+ * + * @param lhs the left hand double[] + * @param rhs the right hand double[] + * @return EqualsBuilder - used to chain calls. + */ + public EqualsBuilder append(double[] lhs, double[] rhs) { + if (isEquals == false) { + return this; + } + if (lhs == rhs) { + return this; + } + if (lhs == null || rhs == null) { + this.setEquals(false); + return this; + } + if (lhs.length != rhs.length) { + this.setEquals(false); + return this; + } + for (int i = 0; i < lhs.length && isEquals; ++i) { + append(lhs[i], rhs[i]); + } + return this; + } + + /** + *

Deep comparison of array of float. Length and all + * values are compared.

+ * + *

The method {@link #append(float, float)} is used.

+ * + * @param lhs the left hand float[] + * @param rhs the right hand float[] + * @return EqualsBuilder - used to chain calls. + */ + public EqualsBuilder append(float[] lhs, float[] rhs) { + if (isEquals == false) { + return this; + } + if (lhs == rhs) { + return this; + } + if (lhs == null || rhs == null) { + this.setEquals(false); + return this; + } + if (lhs.length != rhs.length) { + this.setEquals(false); + return this; + } + for (int i = 0; i < lhs.length && isEquals; ++i) { + append(lhs[i], rhs[i]); + } + return this; + } + + /** + *

Deep comparison of array of boolean. Length and all + * values are compared.

+ * + *

The method {@link #append(boolean, boolean)} is used.

+ * + * @param lhs the left hand boolean[] + * @param rhs the right hand boolean[] + * @return EqualsBuilder - used to chain calls. + */ + public EqualsBuilder append(boolean[] lhs, boolean[] rhs) { + if (isEquals == false) { + return this; + } + if (lhs == rhs) { + return this; + } + if (lhs == null || rhs == null) { + this.setEquals(false); + return this; + } + if (lhs.length != rhs.length) { + this.setEquals(false); + return this; + } + for (int i = 0; i < lhs.length && isEquals; ++i) { + append(lhs[i], rhs[i]); + } + return this; + } + + /** + *

Returns true if the fields that have been checked + * are all equal.

+ * + * @return boolean + */ + public boolean isEquals() { + return this.isEquals; + } + + /** + *

Returns true if the fields that have been checked + * are all equal.

+ * + * @return true if all of the fields that have been checked + * are equal, false otherwise. + * + * @since 3.0 + */ + @Override + public Boolean build() { + return Boolean.valueOf(isEquals()); + } + + /** + * Sets the isEquals value. + * + * @param isEquals The value to set. + * @since 2.1 + */ + protected void setEquals(boolean isEquals) { + this.isEquals = isEquals; + } + + /** + * Reset the EqualsBuilder so you can use the same object again + * @since 2.5 + */ + public void reset() { + this.isEquals = true; + } +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/builder/EqualsBuilderTest.java b/ApacheCommonsLang/org/apache/commons/lang3/builder/EqualsBuilderTest.java new file mode 100644 index 0000000..7470981 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/builder/EqualsBuilderTest.java @@ -0,0 +1,1110 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.builder; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import java.util.Arrays; + +import org.junit.Test; + +/** + * Unit tests {@link org.apache.commons.lang3.builder.EqualsBuilder}. + * + * @version $Id: EqualsBuilderTest.java 1199894 2011-11-09 17:53:59Z ggregory $ + */ +public class EqualsBuilderTest { + + //----------------------------------------------------------------------- + + static class TestObject { + private int a; + public TestObject() { + } + public TestObject(int a) { + this.a = a; + } + @Override + public boolean equals(Object o) { + if (o == null) { return false; } + if (o == this) { return true; } + if (o.getClass() != getClass()) { + return false; + } + + TestObject rhs = (TestObject) o; + return a == rhs.a; + } + + public void setA(int a) { + this.a = a; + } + + public int getA() { + return a; + } + } + + static class TestSubObject extends TestObject { + private int b; + public TestSubObject() { + super(0); + } + public TestSubObject(int a, int b) { + super(a); + this.b = b; + } + @Override + public boolean equals(Object o) { + if (o == null) { return false; } + if (o == this) { return true; } + if (o.getClass() != getClass()) { + return false; + } + + TestSubObject rhs = (TestSubObject) o; + return super.equals(o) && b == rhs.b; + } + + public void setB(int b) { + this.b = b; + } + + public int getB() { + return b; + } + } + + static class TestEmptySubObject extends TestObject { + public TestEmptySubObject(int a) { + super(a); + } + } + + static class TestTSubObject extends TestObject { + @SuppressWarnings("unused") + private transient int t; + public TestTSubObject(int a, int t) { + super(a); + this.t = t; + } + } + + static class TestTTSubObject extends TestTSubObject { + @SuppressWarnings("unused") + private transient int tt; + public TestTTSubObject(int a, int t, int tt) { + super(a, t); + this.tt = tt; + } + } + + static class TestTTLeafObject extends TestTTSubObject { + @SuppressWarnings("unused") + private int leafValue; + public TestTTLeafObject(int a, int t, int tt, int leafValue) { + super(a, t, tt); + this.leafValue = leafValue; + } + } + + static class TestTSubObject2 extends TestObject { + private transient int t; + public TestTSubObject2(int a, int t) { + super(a); + } + public int getT() { + return t; + } + public void setT(int t) { + this.t = t; + } + } + + @Test + public void testReflectionEquals() { + TestObject o1 = new TestObject(4); + TestObject o2 = new TestObject(5); + assertTrue(EqualsBuilder.reflectionEquals(o1, o1)); + assertTrue(!EqualsBuilder.reflectionEquals(o1, o2)); + o2.setA(4); + assertTrue(EqualsBuilder.reflectionEquals(o1, o2)); + + assertTrue(!EqualsBuilder.reflectionEquals(o1, this)); + + assertTrue(!EqualsBuilder.reflectionEquals(o1, null)); + assertTrue(!EqualsBuilder.reflectionEquals(null, o2)); + assertTrue(EqualsBuilder.reflectionEquals((Object) null, (Object) null)); + } + + @Test + public void testReflectionHierarchyEquals() { + testReflectionHierarchyEquals(false); + testReflectionHierarchyEquals(true); + // Transients + assertTrue(EqualsBuilder.reflectionEquals(new TestTTLeafObject(1, 2, 3, 4), new TestTTLeafObject(1, 2, 3, 4), true)); + assertTrue(EqualsBuilder.reflectionEquals(new TestTTLeafObject(1, 2, 3, 4), new TestTTLeafObject(1, 2, 3, 4), false)); + assertTrue(!EqualsBuilder.reflectionEquals(new TestTTLeafObject(1, 0, 0, 4), new TestTTLeafObject(1, 2, 3, 4), true)); + assertTrue(!EqualsBuilder.reflectionEquals(new TestTTLeafObject(1, 2, 3, 4), new TestTTLeafObject(1, 2, 3, 0), true)); + assertTrue(!EqualsBuilder.reflectionEquals(new TestTTLeafObject(0, 2, 3, 4), new TestTTLeafObject(1, 2, 3, 4), true)); + } + + private void testReflectionHierarchyEquals(boolean testTransients) { + TestObject to1 = new TestObject(4); + TestObject to1Bis = new TestObject(4); + TestObject to1Ter = new TestObject(4); + TestObject to2 = new TestObject(5); + TestEmptySubObject teso = new TestEmptySubObject(4); + TestTSubObject ttso = new TestTSubObject(4, 1); + TestTTSubObject tttso = new TestTTSubObject(4, 1, 2); + TestTTLeafObject ttlo = new TestTTLeafObject(4, 1, 2, 3); + TestSubObject tso1 = new TestSubObject(1, 4); + TestSubObject tso1bis = new TestSubObject(1, 4); + TestSubObject tso1ter = new TestSubObject(1, 4); + TestSubObject tso2 = new TestSubObject(2, 5); + + testReflectionEqualsEquivalenceRelationship(to1, to1Bis, to1Ter, to2, new TestObject(), testTransients); + testReflectionEqualsEquivalenceRelationship(tso1, tso1bis, tso1ter, tso2, new TestSubObject(), testTransients); + + // More sanity checks: + + // same values + assertTrue(EqualsBuilder.reflectionEquals(ttlo, ttlo, testTransients)); + assertTrue(EqualsBuilder.reflectionEquals(new TestSubObject(1, 10), new TestSubObject(1, 10), testTransients)); + // same super values, diff sub values + assertTrue(!EqualsBuilder.reflectionEquals(new TestSubObject(1, 10), new TestSubObject(1, 11), testTransients)); + assertTrue(!EqualsBuilder.reflectionEquals(new TestSubObject(1, 11), new TestSubObject(1, 10), testTransients)); + // diff super values, same sub values + assertTrue(!EqualsBuilder.reflectionEquals(new TestSubObject(0, 10), new TestSubObject(1, 10), testTransients)); + assertTrue(!EqualsBuilder.reflectionEquals(new TestSubObject(1, 10), new TestSubObject(0, 10), testTransients)); + + // mix super and sub types: equals + assertTrue(EqualsBuilder.reflectionEquals(to1, teso, testTransients)); + assertTrue(EqualsBuilder.reflectionEquals(teso, to1, testTransients)); + + assertTrue(EqualsBuilder.reflectionEquals(to1, ttso, false)); // Force testTransients = false for this assert + assertTrue(EqualsBuilder.reflectionEquals(ttso, to1, false)); // Force testTransients = false for this assert + + assertTrue(EqualsBuilder.reflectionEquals(to1, tttso, false)); // Force testTransients = false for this assert + assertTrue(EqualsBuilder.reflectionEquals(tttso, to1, false)); // Force testTransients = false for this assert + + assertTrue(EqualsBuilder.reflectionEquals(ttso, tttso, false)); // Force testTransients = false for this assert + assertTrue(EqualsBuilder.reflectionEquals(tttso, ttso, false)); // Force testTransients = false for this assert + + // mix super and sub types: NOT equals + assertTrue(!EqualsBuilder.reflectionEquals(new TestObject(0), new TestEmptySubObject(1), testTransients)); + assertTrue(!EqualsBuilder.reflectionEquals(new TestEmptySubObject(1), new TestObject(0), testTransients)); + + assertTrue(!EqualsBuilder.reflectionEquals(new TestObject(0), new TestTSubObject(1, 1), testTransients)); + assertTrue(!EqualsBuilder.reflectionEquals(new TestTSubObject(1, 1), new TestObject(0), testTransients)); + + assertTrue(!EqualsBuilder.reflectionEquals(new TestObject(1), new TestSubObject(0, 10), testTransients)); + assertTrue(!EqualsBuilder.reflectionEquals(new TestSubObject(0, 10), new TestObject(1), testTransients)); + + assertTrue(!EqualsBuilder.reflectionEquals(to1, ttlo)); + assertTrue(!EqualsBuilder.reflectionEquals(tso1, this)); + } + + /** + * Equivalence relationship tests inspired by "Effective Java": + *
    + *
  • reflection
  • + *
  • symmetry
  • + *
  • transitive
  • + *
  • consistency
  • + *
  • non-null reference
  • + *
+ * @param to a TestObject + * @param toBis a TestObject, equal to to and toTer + * @param toTer Left hand side, equal to to and toBis + * @param to2 a different TestObject + * @param oToChange a TestObject that will be changed + * @param testTransients whether to test transient instance variables + */ + private void testReflectionEqualsEquivalenceRelationship( + TestObject to, + TestObject toBis, + TestObject toTer, + TestObject to2, + TestObject oToChange, + boolean testTransients) { + + // reflection test + assertTrue(EqualsBuilder.reflectionEquals(to, to, testTransients)); + assertTrue(EqualsBuilder.reflectionEquals(to2, to2, testTransients)); + + // symmetry test + assertTrue(EqualsBuilder.reflectionEquals(to, toBis, testTransients) && EqualsBuilder.reflectionEquals(toBis, to, testTransients)); + + // transitive test + assertTrue( + EqualsBuilder.reflectionEquals(to, toBis, testTransients) + && EqualsBuilder.reflectionEquals(toBis, toTer, testTransients) + && EqualsBuilder.reflectionEquals(to, toTer, testTransients)); + + // consistency test + oToChange.setA(to.getA()); + if (oToChange instanceof TestSubObject) { + ((TestSubObject) oToChange).setB(((TestSubObject) to).getB()); + } + assertTrue(EqualsBuilder.reflectionEquals(oToChange, to, testTransients)); + assertTrue(EqualsBuilder.reflectionEquals(oToChange, to, testTransients)); + oToChange.setA(to.getA() + 1); + if (oToChange instanceof TestSubObject) { + ((TestSubObject) oToChange).setB(((TestSubObject) to).getB() + 1); + } + assertTrue(!EqualsBuilder.reflectionEquals(oToChange, to, testTransients)); + assertTrue(!EqualsBuilder.reflectionEquals(oToChange, to, testTransients)); + + // non-null reference test + assertTrue(!EqualsBuilder.reflectionEquals(to, null, testTransients)); + assertTrue(!EqualsBuilder.reflectionEquals(to2, null, testTransients)); + assertTrue(!EqualsBuilder.reflectionEquals(null, to, testTransients)); + assertTrue(!EqualsBuilder.reflectionEquals(null, to2, testTransients)); + assertTrue(EqualsBuilder.reflectionEquals((Object) null, (Object) null, testTransients)); + } + + @Test + public void testSuper() { + TestObject o1 = new TestObject(4); + TestObject o2 = new TestObject(5); + assertTrue(new EqualsBuilder().appendSuper(true).append(o1, o1).isEquals()); + assertFalse(new EqualsBuilder().appendSuper(false).append(o1, o1).isEquals()); + assertFalse(new EqualsBuilder().appendSuper(true).append(o1, o2).isEquals()); + assertFalse(new EqualsBuilder().appendSuper(false).append(o1, o2).isEquals()); + } + + @Test + public void testObject() { + TestObject o1 = new TestObject(4); + TestObject o2 = new TestObject(5); + assertTrue(new EqualsBuilder().append(o1, o1).isEquals()); + assertTrue(!new EqualsBuilder().append(o1, o2).isEquals()); + o2.setA(4); + assertTrue(new EqualsBuilder().append(o1, o2).isEquals()); + + assertTrue(!new EqualsBuilder().append(o1, this).isEquals()); + + assertTrue(!new EqualsBuilder().append(o1, null).isEquals()); + assertTrue(!new EqualsBuilder().append(null, o2).isEquals()); + assertTrue(new EqualsBuilder().append((Object) null, (Object) null).isEquals()); + } + + @Test + public void testObjectBuild() { + TestObject o1 = new TestObject(4); + TestObject o2 = new TestObject(5); + assertEquals(Boolean.TRUE, new EqualsBuilder().append(o1, o1).build()); + assertEquals(Boolean.FALSE, new EqualsBuilder().append(o1, o2).build()); + o2.setA(4); + assertEquals(Boolean.TRUE, new EqualsBuilder().append(o1, o2).build()); + + assertEquals(Boolean.FALSE, new EqualsBuilder().append(o1, this).build()); + + assertEquals(Boolean.FALSE, new EqualsBuilder().append(o1, null).build()); + assertEquals(Boolean.FALSE, new EqualsBuilder().append(null, o2).build()); + assertEquals(Boolean.TRUE, new EqualsBuilder().append((Object) null, (Object) null).build()); + } + + @Test + public void testLong() { + long o1 = 1L; + long o2 = 2L; + assertTrue(new EqualsBuilder().append(o1, o1).isEquals()); + assertTrue(!new EqualsBuilder().append(o1, o2).isEquals()); + } + + @Test + public void testInt() { + int o1 = 1; + int o2 = 2; + assertTrue(new EqualsBuilder().append(o1, o1).isEquals()); + assertTrue(!new EqualsBuilder().append(o1, o2).isEquals()); + } + + @Test + public void testShort() { + short o1 = 1; + short o2 = 2; + assertTrue(new EqualsBuilder().append(o1, o1).isEquals()); + assertTrue(!new EqualsBuilder().append(o1, o2).isEquals()); + } + + @Test + public void testChar() { + char o1 = 1; + char o2 = 2; + assertTrue(new EqualsBuilder().append(o1, o1).isEquals()); + assertTrue(!new EqualsBuilder().append(o1, o2).isEquals()); + } + + @Test + public void testByte() { + byte o1 = 1; + byte o2 = 2; + assertTrue(new EqualsBuilder().append(o1, o1).isEquals()); + assertTrue(!new EqualsBuilder().append(o1, o2).isEquals()); + } + + @Test + public void testDouble() { + double o1 = 1; + double o2 = 2; + assertTrue(new EqualsBuilder().append(o1, o1).isEquals()); + assertTrue(!new EqualsBuilder().append(o1, o2).isEquals()); + assertTrue(!new EqualsBuilder().append(o1, Double.NaN).isEquals()); + assertTrue(new EqualsBuilder().append(Double.NaN, Double.NaN).isEquals()); + assertTrue(new EqualsBuilder().append(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY).isEquals()); + } + + @Test + public void testFloat() { + float o1 = 1; + float o2 = 2; + assertTrue(new EqualsBuilder().append(o1, o1).isEquals()); + assertTrue(!new EqualsBuilder().append(o1, o2).isEquals()); + assertTrue(!new EqualsBuilder().append(o1, Float.NaN).isEquals()); + assertTrue(new EqualsBuilder().append(Float.NaN, Float.NaN).isEquals()); + assertTrue(new EqualsBuilder().append(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY).isEquals()); + } + + @Test + public void testAccessors() { + EqualsBuilder equalsBuilder = new EqualsBuilder(); + assertTrue(equalsBuilder.isEquals()); + equalsBuilder.setEquals(true); + assertTrue(equalsBuilder.isEquals()); + equalsBuilder.setEquals(false); + assertFalse(equalsBuilder.isEquals()); + } + + @Test + public void testReset() { + EqualsBuilder equalsBuilder = new EqualsBuilder(); + assertTrue(equalsBuilder.isEquals()); + equalsBuilder.setEquals(false); + assertFalse(equalsBuilder.isEquals()); + equalsBuilder.reset(); + assertTrue(equalsBuilder.isEquals()); + } + + @Test + public void testBoolean() { + boolean o1 = true; + boolean o2 = false; + assertTrue(new EqualsBuilder().append(o1, o1).isEquals()); + assertTrue(!new EqualsBuilder().append(o1, o2).isEquals()); + } + + @Test + public void testObjectArray() { + TestObject[] obj1 = new TestObject[3]; + obj1[0] = new TestObject(4); + obj1[1] = new TestObject(5); + obj1[2] = null; + TestObject[] obj2 = new TestObject[3]; + obj2[0] = new TestObject(4); + obj2[1] = new TestObject(5); + obj2[2] = null; + + assertTrue(new EqualsBuilder().append(obj1, obj1).isEquals()); + assertTrue(new EqualsBuilder().append(obj2, obj2).isEquals()); + assertTrue(new EqualsBuilder().append(obj1, obj2).isEquals()); + obj1[1].setA(6); + assertTrue(!new EqualsBuilder().append(obj1, obj2).isEquals()); + obj1[1].setA(5); + assertTrue(new EqualsBuilder().append(obj1, obj2).isEquals()); + obj1[2] = obj1[1]; + assertTrue(!new EqualsBuilder().append(obj1, obj2).isEquals()); + obj1[2] = null; + assertTrue(new EqualsBuilder().append(obj1, obj2).isEquals()); + + obj2 = null; + assertTrue(!new EqualsBuilder().append(obj1, obj2).isEquals()); + obj1 = null; + assertTrue(new EqualsBuilder().append(obj1, obj2).isEquals()); + } + + @Test + public void testLongArray() { + long[] obj1 = new long[2]; + obj1[0] = 5L; + obj1[1] = 6L; + long[] obj2 = new long[2]; + obj2[0] = 5L; + obj2[1] = 6L; + assertTrue(new EqualsBuilder().append(obj1, obj1).isEquals()); + assertTrue(new EqualsBuilder().append(obj1, obj2).isEquals()); + obj1[1] = 7; + assertTrue(!new EqualsBuilder().append(obj1, obj2).isEquals()); + + obj2 = null; + assertTrue(!new EqualsBuilder().append(obj1, obj2).isEquals()); + obj1 = null; + assertTrue(new EqualsBuilder().append(obj1, obj2).isEquals()); + } + + @Test + public void testIntArray() { + int[] obj1 = new int[2]; + obj1[0] = 5; + obj1[1] = 6; + int[] obj2 = new int[2]; + obj2[0] = 5; + obj2[1] = 6; + assertTrue(new EqualsBuilder().append(obj1, obj1).isEquals()); + assertTrue(new EqualsBuilder().append(obj1, obj2).isEquals()); + obj1[1] = 7; + assertTrue(!new EqualsBuilder().append(obj1, obj2).isEquals()); + + obj2 = null; + assertTrue(!new EqualsBuilder().append(obj1, obj2).isEquals()); + obj1 = null; + assertTrue(new EqualsBuilder().append(obj1, obj2).isEquals()); + } + + @Test + public void testShortArray() { + short[] obj1 = new short[2]; + obj1[0] = 5; + obj1[1] = 6; + short[] obj2 = new short[2]; + obj2[0] = 5; + obj2[1] = 6; + assertTrue(new EqualsBuilder().append(obj1, obj1).isEquals()); + assertTrue(new EqualsBuilder().append(obj1, obj2).isEquals()); + obj1[1] = 7; + assertTrue(!new EqualsBuilder().append(obj1, obj2).isEquals()); + + obj2 = null; + assertTrue(!new EqualsBuilder().append(obj1, obj2).isEquals()); + obj1 = null; + assertTrue(new EqualsBuilder().append(obj1, obj2).isEquals()); + } + + @Test + public void testCharArray() { + char[] obj1 = new char[2]; + obj1[0] = 5; + obj1[1] = 6; + char[] obj2 = new char[2]; + obj2[0] = 5; + obj2[1] = 6; + assertTrue(new EqualsBuilder().append(obj1, obj1).isEquals()); + assertTrue(new EqualsBuilder().append(obj1, obj2).isEquals()); + obj1[1] = 7; + assertTrue(!new EqualsBuilder().append(obj1, obj2).isEquals()); + + obj2 = null; + assertTrue(!new EqualsBuilder().append(obj1, obj2).isEquals()); + obj1 = null; + assertTrue(new EqualsBuilder().append(obj1, obj2).isEquals()); + } + + @Test + public void testByteArray() { + byte[] obj1 = new byte[2]; + obj1[0] = 5; + obj1[1] = 6; + byte[] obj2 = new byte[2]; + obj2[0] = 5; + obj2[1] = 6; + assertTrue(new EqualsBuilder().append(obj1, obj1).isEquals()); + assertTrue(new EqualsBuilder().append(obj1, obj2).isEquals()); + obj1[1] = 7; + assertTrue(!new EqualsBuilder().append(obj1, obj2).isEquals()); + + obj2 = null; + assertTrue(!new EqualsBuilder().append(obj1, obj2).isEquals()); + obj1 = null; + assertTrue(new EqualsBuilder().append(obj1, obj2).isEquals()); + } + + @Test + public void testDoubleArray() { + double[] obj1 = new double[2]; + obj1[0] = 5; + obj1[1] = 6; + double[] obj2 = new double[2]; + obj2[0] = 5; + obj2[1] = 6; + assertTrue(new EqualsBuilder().append(obj1, obj1).isEquals()); + assertTrue(new EqualsBuilder().append(obj1, obj2).isEquals()); + obj1[1] = 7; + assertTrue(!new EqualsBuilder().append(obj1, obj2).isEquals()); + + obj2 = null; + assertTrue(!new EqualsBuilder().append(obj1, obj2).isEquals()); + obj1 = null; + assertTrue(new EqualsBuilder().append(obj1, obj2).isEquals()); + } + + @Test + public void testFloatArray() { + float[] obj1 = new float[2]; + obj1[0] = 5; + obj1[1] = 6; + float[] obj2 = new float[2]; + obj2[0] = 5; + obj2[1] = 6; + assertTrue(new EqualsBuilder().append(obj1, obj1).isEquals()); + assertTrue(new EqualsBuilder().append(obj1, obj2).isEquals()); + obj1[1] = 7; + assertTrue(!new EqualsBuilder().append(obj1, obj2).isEquals()); + + obj2 = null; + assertTrue(!new EqualsBuilder().append(obj1, obj2).isEquals()); + obj1 = null; + assertTrue(new EqualsBuilder().append(obj1, obj2).isEquals()); + } + + @Test + public void testBooleanArray() { + boolean[] obj1 = new boolean[2]; + obj1[0] = true; + obj1[1] = false; + boolean[] obj2 = new boolean[2]; + obj2[0] = true; + obj2[1] = false; + assertTrue(new EqualsBuilder().append(obj1, obj1).isEquals()); + assertTrue(new EqualsBuilder().append(obj1, obj2).isEquals()); + obj1[1] = true; + assertTrue(!new EqualsBuilder().append(obj1, obj2).isEquals()); + + obj2 = null; + assertTrue(!new EqualsBuilder().append(obj1, obj2).isEquals()); + obj1 = null; + assertTrue(new EqualsBuilder().append(obj1, obj2).isEquals()); + } + + @Test + public void testMultiLongArray() { + long[][] array1 = new long[2][2]; + long[][] array2 = new long[2][2]; + for (int i = 0; i < array1.length; ++i) { + for (int j = 0; j < array1[0].length; j++) { + array1[i][j] = (i + 1) * (j + 1); + array2[i][j] = (i + 1) * (j + 1); + } + } + assertTrue(new EqualsBuilder().append(array1, array1).isEquals()); + assertTrue(new EqualsBuilder().append(array1, array2).isEquals()); + array1[1][1] = 0; + assertTrue(!new EqualsBuilder().append(array1, array2).isEquals()); + } + + @Test + public void testMultiIntArray() { + int[][] array1 = new int[2][2]; + int[][] array2 = new int[2][2]; + for (int i = 0; i < array1.length; ++i) { + for (int j = 0; j < array1[0].length; j++) { + array1[i][j] = (i + 1) * (j + 1); + array2[i][j] = (i + 1) * (j + 1); + } + } + assertTrue(new EqualsBuilder().append(array1, array1).isEquals()); + assertTrue(new EqualsBuilder().append(array1, array2).isEquals()); + array1[1][1] = 0; + assertTrue(!new EqualsBuilder().append(array1, array2).isEquals()); + } + + @Test + public void testMultiShortArray() { + short[][] array1 = new short[2][2]; + short[][] array2 = new short[2][2]; + for (short i = 0; i < array1.length; ++i) { + for (short j = 0; j < array1[0].length; j++) { + array1[i][j] = i; + array2[i][j] = i; + } + } + assertTrue(new EqualsBuilder().append(array1, array1).isEquals()); + assertTrue(new EqualsBuilder().append(array1, array2).isEquals()); + array1[1][1] = 0; + assertTrue(!new EqualsBuilder().append(array1, array2).isEquals()); + } + + @Test + public void testMultiCharArray() { + char[][] array1 = new char[2][2]; + char[][] array2 = new char[2][2]; + for (char i = 0; i < array1.length; ++i) { + for (char j = 0; j < array1[0].length; j++) { + array1[i][j] = i; + array2[i][j] = i; + } + } + assertTrue(new EqualsBuilder().append(array1, array1).isEquals()); + assertTrue(new EqualsBuilder().append(array1, array2).isEquals()); + array1[1][1] = 0; + assertTrue(!new EqualsBuilder().append(array1, array2).isEquals()); + } + + @Test + public void testMultiByteArray() { + byte[][] array1 = new byte[2][2]; + byte[][] array2 = new byte[2][2]; + for (byte i = 0; i < array1.length; ++i) { + for (byte j = 0; j < array1[0].length; j++) { + array1[i][j] = i; + array2[i][j] = i; + } + } + assertTrue(new EqualsBuilder().append(array1, array1).isEquals()); + assertTrue(new EqualsBuilder().append(array1, array2).isEquals()); + array1[1][1] = 0; + assertTrue(!new EqualsBuilder().append(array1, array2).isEquals()); + } + + @Test + public void testMultiFloatArray() { + float[][] array1 = new float[2][2]; + float[][] array2 = new float[2][2]; + for (int i = 0; i < array1.length; ++i) { + for (int j = 0; j < array1[0].length; j++) { + array1[i][j] = (i + 1) * (j + 1); + array2[i][j] = (i + 1) * (j + 1); + } + } + assertTrue(new EqualsBuilder().append(array1, array1).isEquals()); + assertTrue(new EqualsBuilder().append(array1, array2).isEquals()); + array1[1][1] = 0; + assertTrue(!new EqualsBuilder().append(array1, array2).isEquals()); + } + + @Test + public void testMultiDoubleArray() { + double[][] array1 = new double[2][2]; + double[][] array2 = new double[2][2]; + for (int i = 0; i < array1.length; ++i) { + for (int j = 0; j < array1[0].length; j++) { + array1[i][j] = (i + 1) * (j + 1); + array2[i][j] = (i + 1) * (j + 1); + } + } + assertTrue(new EqualsBuilder().append(array1, array1).isEquals()); + assertTrue(new EqualsBuilder().append(array1, array2).isEquals()); + array1[1][1] = 0; + assertTrue(!new EqualsBuilder().append(array1, array2).isEquals()); + } + + @Test + public void testMultiBooleanArray() { + boolean[][] array1 = new boolean[2][2]; + boolean[][] array2 = new boolean[2][2]; + for (int i = 0; i < array1.length; ++i) { + for (int j = 0; j < array1[0].length; j++) { + array1[i][j] = i == 1 || j == 1; + array2[i][j] = i == 1 || j == 1; + } + } + assertTrue(new EqualsBuilder().append(array1, array1).isEquals()); + assertTrue(new EqualsBuilder().append(array1, array2).isEquals()); + array1[1][1] = false; + assertTrue(!new EqualsBuilder().append(array1, array2).isEquals()); + + // compare 1 dim to 2. + boolean[] array3 = new boolean[]{true, true}; + assertFalse(new EqualsBuilder().append(array1, array3).isEquals()); + assertFalse(new EqualsBuilder().append(array3, array1).isEquals()); + assertFalse(new EqualsBuilder().append(array2, array3).isEquals()); + assertFalse(new EqualsBuilder().append(array3, array2).isEquals()); + } + + @Test + public void testRaggedArray() { + long array1[][] = new long[2][]; + long array2[][] = new long[2][]; + for (int i = 0; i < array1.length; ++i) { + array1[i] = new long[2]; + array2[i] = new long[2]; + for (int j = 0; j < array1[i].length; ++j) { + array1[i][j] = (i + 1) * (j + 1); + array2[i][j] = (i + 1) * (j + 1); + } + } + assertTrue(new EqualsBuilder().append(array1, array1).isEquals()); + assertTrue(new EqualsBuilder().append(array1, array2).isEquals()); + array1[1][1] = 0; + assertTrue(!new EqualsBuilder().append(array1, array2).isEquals()); + } + + @Test + public void testMixedArray() { + Object array1[] = new Object[2]; + Object array2[] = new Object[2]; + for (int i = 0; i < array1.length; ++i) { + array1[i] = new long[2]; + array2[i] = new long[2]; + for (int j = 0; j < 2; ++j) { + ((long[]) array1[i])[j] = (i + 1) * (j + 1); + ((long[]) array2[i])[j] = (i + 1) * (j + 1); + } + } + assertTrue(new EqualsBuilder().append(array1, array1).isEquals()); + assertTrue(new EqualsBuilder().append(array1, array2).isEquals()); + ((long[]) array1[1])[1] = 0; + assertTrue(!new EqualsBuilder().append(array1, array2).isEquals()); + } + + @Test + public void testObjectArrayHiddenByObject() { + TestObject[] array1 = new TestObject[2]; + array1[0] = new TestObject(4); + array1[1] = new TestObject(5); + TestObject[] array2 = new TestObject[2]; + array2[0] = new TestObject(4); + array2[1] = new TestObject(5); + Object obj1 = array1; + Object obj2 = array2; + assertTrue(new EqualsBuilder().append(obj1, obj1).isEquals()); + assertTrue(new EqualsBuilder().append(obj1, array1).isEquals()); + assertTrue(new EqualsBuilder().append(obj1, obj2).isEquals()); + assertTrue(new EqualsBuilder().append(obj1, array2).isEquals()); + array1[1].setA(6); + assertTrue(!new EqualsBuilder().append(obj1, obj2).isEquals()); + } + + @Test + public void testLongArrayHiddenByObject() { + long[] array1 = new long[2]; + array1[0] = 5L; + array1[1] = 6L; + long[] array2 = new long[2]; + array2[0] = 5L; + array2[1] = 6L; + Object obj1 = array1; + Object obj2 = array2; + assertTrue(new EqualsBuilder().append(obj1, obj1).isEquals()); + assertTrue(new EqualsBuilder().append(obj1, array1).isEquals()); + assertTrue(new EqualsBuilder().append(obj1, obj2).isEquals()); + assertTrue(new EqualsBuilder().append(obj1, array2).isEquals()); + array1[1] = 7; + assertTrue(!new EqualsBuilder().append(obj1, obj2).isEquals()); + } + + @Test + public void testIntArrayHiddenByObject() { + int[] array1 = new int[2]; + array1[0] = 5; + array1[1] = 6; + int[] array2 = new int[2]; + array2[0] = 5; + array2[1] = 6; + Object obj1 = array1; + Object obj2 = array2; + assertTrue(new EqualsBuilder().append(obj1, obj1).isEquals()); + assertTrue(new EqualsBuilder().append(obj1, array1).isEquals()); + assertTrue(new EqualsBuilder().append(obj1, obj2).isEquals()); + assertTrue(new EqualsBuilder().append(obj1, array2).isEquals()); + array1[1] = 7; + assertTrue(!new EqualsBuilder().append(obj1, obj2).isEquals()); + } + + @Test + public void testShortArrayHiddenByObject() { + short[] array1 = new short[2]; + array1[0] = 5; + array1[1] = 6; + short[] array2 = new short[2]; + array2[0] = 5; + array2[1] = 6; + Object obj1 = array1; + Object obj2 = array2; + assertTrue(new EqualsBuilder().append(obj1, obj1).isEquals()); + assertTrue(new EqualsBuilder().append(obj1, array1).isEquals()); + assertTrue(new EqualsBuilder().append(obj1, obj2).isEquals()); + assertTrue(new EqualsBuilder().append(obj1, array2).isEquals()); + array1[1] = 7; + assertTrue(!new EqualsBuilder().append(obj1, obj2).isEquals()); + } + + @Test + public void testCharArrayHiddenByObject() { + char[] array1 = new char[2]; + array1[0] = 5; + array1[1] = 6; + char[] array2 = new char[2]; + array2[0] = 5; + array2[1] = 6; + Object obj1 = array1; + Object obj2 = array2; + assertTrue(new EqualsBuilder().append(obj1, obj1).isEquals()); + assertTrue(new EqualsBuilder().append(obj1, array1).isEquals()); + assertTrue(new EqualsBuilder().append(obj1, obj2).isEquals()); + assertTrue(new EqualsBuilder().append(obj1, array2).isEquals()); + array1[1] = 7; + assertTrue(!new EqualsBuilder().append(obj1, obj2).isEquals()); + } + + @Test + public void testByteArrayHiddenByObject() { + byte[] array1 = new byte[2]; + array1[0] = 5; + array1[1] = 6; + byte[] array2 = new byte[2]; + array2[0] = 5; + array2[1] = 6; + Object obj1 = array1; + Object obj2 = array2; + assertTrue(new EqualsBuilder().append(obj1, obj1).isEquals()); + assertTrue(new EqualsBuilder().append(obj1, array1).isEquals()); + assertTrue(new EqualsBuilder().append(obj1, obj2).isEquals()); + assertTrue(new EqualsBuilder().append(obj1, array2).isEquals()); + array1[1] = 7; + assertTrue(!new EqualsBuilder().append(obj1, obj2).isEquals()); + } + + @Test + public void testDoubleArrayHiddenByObject() { + double[] array1 = new double[2]; + array1[0] = 5; + array1[1] = 6; + double[] array2 = new double[2]; + array2[0] = 5; + array2[1] = 6; + Object obj1 = array1; + Object obj2 = array2; + assertTrue(new EqualsBuilder().append(obj1, obj1).isEquals()); + assertTrue(new EqualsBuilder().append(obj1, array1).isEquals()); + assertTrue(new EqualsBuilder().append(obj1, obj2).isEquals()); + assertTrue(new EqualsBuilder().append(obj1, array2).isEquals()); + array1[1] = 7; + assertTrue(!new EqualsBuilder().append(obj1, obj2).isEquals()); + } + + @Test + public void testFloatArrayHiddenByObject() { + float[] array1 = new float[2]; + array1[0] = 5; + array1[1] = 6; + float[] array2 = new float[2]; + array2[0] = 5; + array2[1] = 6; + Object obj1 = array1; + Object obj2 = array2; + assertTrue(new EqualsBuilder().append(obj1, obj1).isEquals()); + assertTrue(new EqualsBuilder().append(obj1, array1).isEquals()); + assertTrue(new EqualsBuilder().append(obj1, obj2).isEquals()); + assertTrue(new EqualsBuilder().append(obj1, array2).isEquals()); + array1[1] = 7; + assertTrue(!new EqualsBuilder().append(obj1, obj2).isEquals()); + } + + @Test + public void testBooleanArrayHiddenByObject() { + boolean[] array1 = new boolean[2]; + array1[0] = true; + array1[1] = false; + boolean[] array2 = new boolean[2]; + array2[0] = true; + array2[1] = false; + Object obj1 = array1; + Object obj2 = array2; + assertTrue(new EqualsBuilder().append(obj1, obj1).isEquals()); + assertTrue(new EqualsBuilder().append(obj1, array1).isEquals()); + assertTrue(new EqualsBuilder().append(obj1, obj2).isEquals()); + assertTrue(new EqualsBuilder().append(obj1, array2).isEquals()); + array1[1] = true; + assertTrue(!new EqualsBuilder().append(obj1, obj2).isEquals()); + } + + public static class TestACanEqualB { + private int a; + + public TestACanEqualB(int a) { + this.a = a; + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (o instanceof TestACanEqualB) { + return this.a == ((TestACanEqualB) o).getA(); + } + if (o instanceof TestBCanEqualA) { + return this.a == ((TestBCanEqualA) o).getB(); + } + return false; + } + + public int getA() { + return this.a; + } + } + + public static class TestBCanEqualA { + private int b; + + public TestBCanEqualA(int b) { + this.b = b; + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (o instanceof TestACanEqualB) { + return this.b == ((TestACanEqualB) o).getA(); + } + if (o instanceof TestBCanEqualA) { + return this.b == ((TestBCanEqualA) o).getB(); + } + return false; + } + + public int getB() { + return this.b; + } + } + + /** + * Tests two instances of classes that can be equal and that are not "related". The two classes are not subclasses + * of each other and do not share a parent aside from Object. + * See http://issues.apache.org/bugzilla/show_bug.cgi?id=33069 + */ + @Test + public void testUnrelatedClasses() { + Object[] x = new Object[]{new TestACanEqualB(1)}; + Object[] y = new Object[]{new TestBCanEqualA(1)}; + + // sanity checks: + assertTrue(Arrays.equals(x, x)); + assertTrue(Arrays.equals(y, y)); + assertTrue(Arrays.equals(x, y)); + assertTrue(Arrays.equals(y, x)); + // real tests: + assertTrue(x[0].equals(x[0])); + assertTrue(y[0].equals(y[0])); + assertTrue(x[0].equals(y[0])); + assertTrue(y[0].equals(x[0])); + assertTrue(new EqualsBuilder().append(x, x).isEquals()); + assertTrue(new EqualsBuilder().append(y, y).isEquals()); + assertTrue(new EqualsBuilder().append(x, y).isEquals()); + assertTrue(new EqualsBuilder().append(y, x).isEquals()); + } + + /** + * Test from http://issues.apache.org/bugzilla/show_bug.cgi?id=33067 + */ + @Test + public void testNpeForNullElement() { + Object[] x1 = new Object[] { Integer.valueOf(1), null, Integer.valueOf(3) }; + Object[] x2 = new Object[] { Integer.valueOf(1), Integer.valueOf(2), Integer.valueOf(3) }; + + // causes an NPE in 2.0 according to: + // http://issues.apache.org/bugzilla/show_bug.cgi?id=33067 + new EqualsBuilder().append(x1, x2); + } + + @Test + public void testReflectionEqualsExcludeFields() throws Exception { + TestObjectWithMultipleFields x1 = new TestObjectWithMultipleFields(1, 2, 3); + TestObjectWithMultipleFields x2 = new TestObjectWithMultipleFields(1, 3, 4); + + // not equal when including all fields + assertTrue(!EqualsBuilder.reflectionEquals(x1, x2)); + + // doesn't barf on null, empty array, or non-existent field, but still tests as not equal + assertTrue(!EqualsBuilder.reflectionEquals(x1, x2, (String[]) null)); + assertTrue(!EqualsBuilder.reflectionEquals(x1, x2, new String[] {})); + assertTrue(!EqualsBuilder.reflectionEquals(x1, x2, new String[] {"xxx"})); + + // not equal if only one of the differing fields excluded + assertTrue(!EqualsBuilder.reflectionEquals(x1, x2, new String[] {"two"})); + assertTrue(!EqualsBuilder.reflectionEquals(x1, x2, new String[] {"three"})); + + // equal if both differing fields excluded + assertTrue(EqualsBuilder.reflectionEquals(x1, x2, new String[] {"two", "three"})); + + // still equal as long as both differing fields are among excluded + assertTrue(EqualsBuilder.reflectionEquals(x1, x2, new String[] {"one", "two", "three"})); + assertTrue(EqualsBuilder.reflectionEquals(x1, x2, new String[] {"one", "two", "three", "xxx"})); + } + + static class TestObjectWithMultipleFields { + @SuppressWarnings("unused") + private TestObject one; + @SuppressWarnings("unused") + private TestObject two; + @SuppressWarnings("unused") + private TestObject three; + + public TestObjectWithMultipleFields(int one, int two, int three) { + this.one = new TestObject(one); + this.two = new TestObject(two); + this.three = new TestObject(three); + } + } + + /** + * Test cyclical object references which cause a StackOverflowException if + * not handled properly. s. LANG-606 + */ + @Test + public void testCyclicalObjectReferences() { + TestObjectReference refX1 = new TestObjectReference(1); + TestObjectReference x1 = new TestObjectReference(1); + x1.setObjectReference(refX1); + refX1.setObjectReference(x1); + + TestObjectReference refX2 = new TestObjectReference(1); + TestObjectReference x2 = new TestObjectReference(1); + x2.setObjectReference(refX2); + refX2.setObjectReference(x2); + + TestObjectReference refX3 = new TestObjectReference(2); + TestObjectReference x3 = new TestObjectReference(2); + x3.setObjectReference(refX3); + refX3.setObjectReference(x3); + + assertTrue(x1.equals(x2)); + assertNull(EqualsBuilder.getRegistry()); + assertFalse(x1.equals(x3)); + assertNull(EqualsBuilder.getRegistry()); + assertFalse(x2.equals(x3)); + assertNull(EqualsBuilder.getRegistry()); + } + + static class TestObjectReference { + @SuppressWarnings("unused") + private TestObjectReference reference; + @SuppressWarnings("unused") + private TestObject one; + + public TestObjectReference(int one) { + this.one = new TestObject(one); + } + + public void setObjectReference(TestObjectReference reference) { + this.reference = reference; + } + + @Override + public boolean equals(Object obj) { + return EqualsBuilder.reflectionEquals(this, obj); + } + } +} + diff --git a/ApacheCommonsLang/org/apache/commons/lang3/builder/HashCodeBuilder.java b/ApacheCommonsLang/org/apache/commons/lang3/builder/HashCodeBuilder.java new file mode 100644 index 0000000..99ef8ca --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/builder/HashCodeBuilder.java @@ -0,0 +1,962 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.lang3.builder; + +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +import org.apache.commons.lang3.ArrayUtils; + +/** + *

+ * Assists in implementing {@link Object#hashCode()} methods. + *

+ * + *

+ * This class enables a good hashCode method to be built for any class. It follows the rules laid out in + * the book Effective Java by Joshua Bloch. Writing a + * good hashCode method is actually quite difficult. This class aims to simplify the process. + *

+ * + *

+ * The following is the approach taken. When appending a data field, the current total is multiplied by the + * multiplier then a relevant value + * for that data type is added. For example, if the current hashCode is 17, and the multiplier is 37, then + * appending the integer 45 will create a hashcode of 674, namely 17 * 37 + 45. + *

+ * + *

+ * All relevant fields from the object should be included in the hashCode method. Derived fields may be + * excluded. In general, any field used in the equals method must be used in the hashCode + * method. + *

+ * + *

+ * To use this class write code as follows: + *

+ * + *
+ * public class Person {
+ *   String name;
+ *   int age;
+ *   boolean smoker;
+ *   ...
+ *
+ *   public int hashCode() {
+ *     // you pick a hard-coded, randomly chosen, non-zero, odd number
+ *     // ideally different for each class
+ *     return new HashCodeBuilder(17, 37).
+ *       append(name).
+ *       append(age).
+ *       append(smoker).
+ *       toHashCode();
+ *   }
+ * }
+ * 
+ * + *

+ * If required, the superclass hashCode() can be added using {@link #appendSuper}. + *

+ * + *

+ * Alternatively, there is a method that uses reflection to determine the fields to test. Because these fields are + * usually private, the method, reflectionHashCode, uses AccessibleObject.setAccessible + * to change the visibility of the fields. This will fail under a security manager, unless the appropriate permissions + * are set up correctly. It is also slower than testing explicitly. + *

+ * + *

+ * A typical invocation for this method would look like: + *

+ * + *
+ * public int hashCode() {
+ *   return HashCodeBuilder.reflectionHashCode(this);
+ * }
+ * 
+ * + * @since 1.0 + * @version $Id: HashCodeBuilder.java 1309977 2012-04-05 17:53:39Z ggregory $ + */ +public class HashCodeBuilder implements Builder { + /** + *

+ * A registry of objects used by reflection methods to detect cyclical object references and avoid infinite loops. + *

+ * + * @since 2.3 + */ + private static final ThreadLocal> REGISTRY = new ThreadLocal>(); + + /* + * NOTE: we cannot store the actual objects in a HashSet, as that would use the very hashCode() + * we are in the process of calculating. + * + * So we generate a one-to-one mapping from the original object to a new object. + * + * Now HashSet uses equals() to determine if two elements with the same hashcode really + * are equal, so we also need to ensure that the replacement objects are only equal + * if the original objects are identical. + * + * The original implementation (2.4 and before) used the System.indentityHashCode() + * method - however this is not guaranteed to generate unique ids (e.g. LANG-459) + * + * We now use the IDKey helper class (adapted from org.apache.axis.utils.IDKey) + * to disambiguate the duplicate ids. + */ + + /** + *

+ * Returns the registry of objects being traversed by the reflection methods in the current thread. + *

+ * + * @return Set the registry of objects being traversed + * @since 2.3 + */ + static Set getRegistry() { + return REGISTRY.get(); + } + + /** + *

+ * Returns true if the registry contains the given object. Used by the reflection methods to avoid + * infinite loops. + *

+ * + * @param value + * The object to lookup in the registry. + * @return boolean true if the registry contains the given object. + * @since 2.3 + */ + static boolean isRegistered(Object value) { + Set registry = getRegistry(); + return registry != null && registry.contains(new IDKey(value)); + } + + /** + *

+ * Appends the fields and values defined by the given object of the given Class. + *

+ * + * @param object + * the object to append details of + * @param clazz + * the class to append details of + * @param builder + * the builder to append to + * @param useTransients + * whether to use transient fields + * @param excludeFields + * Collection of String field names to exclude from use in calculation of hash code + */ + private static void reflectionAppend(Object object, Class clazz, HashCodeBuilder builder, boolean useTransients, + String[] excludeFields) { + if (isRegistered(object)) { + return; + } + try { + register(object); + Field[] fields = clazz.getDeclaredFields(); + AccessibleObject.setAccessible(fields, true); + for (Field field : fields) { + if (!ArrayUtils.contains(excludeFields, field.getName()) + && (field.getName().indexOf('$') == -1) + && (useTransients || !Modifier.isTransient(field.getModifiers())) + && (!Modifier.isStatic(field.getModifiers()))) { + try { + Object fieldValue = field.get(object); + builder.append(fieldValue); + } catch (IllegalAccessException e) { + // this can't happen. Would get a Security exception instead + // throw a runtime exception in case the impossible happens. + throw new InternalError("Unexpected IllegalAccessException"); + } + } + } + } finally { + unregister(object); + } + } + + /** + *

+ * This method uses reflection to build a valid hash code. + *

+ * + *

+ * It uses AccessibleObject.setAccessible to gain access to private fields. This means that it will + * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is + * also not as efficient as testing explicitly. + *

+ * + *

+ * Transient members will be not be used, as they are likely derived fields, and not part of the value of the + * Object. + *

+ * + *

+ * Static fields will not be tested. Superclass fields will be included. + *

+ * + *

+ * Two randomly chosen, non-zero, odd numbers must be passed in. Ideally these should be different for each class, + * however this is not vital. Prime numbers are preferred, especially for the multiplier. + *

+ * + * @param initialNonZeroOddNumber + * a non-zero, odd number used as the initial value + * @param multiplierNonZeroOddNumber + * a non-zero, odd number used as the multiplier + * @param object + * the Object to create a hashCode for + * @return int hash code + * @throws IllegalArgumentException + * if the Object is null + * @throws IllegalArgumentException + * if the number is zero or even + */ + public static int reflectionHashCode(int initialNonZeroOddNumber, int multiplierNonZeroOddNumber, Object object) { + return reflectionHashCode(initialNonZeroOddNumber, multiplierNonZeroOddNumber, object, false, null); + } + + /** + *

+ * This method uses reflection to build a valid hash code. + *

+ * + *

+ * It uses AccessibleObject.setAccessible to gain access to private fields. This means that it will + * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is + * also not as efficient as testing explicitly. + *

+ * + *

+ * If the TestTransients parameter is set to true, transient members will be tested, otherwise they + * are ignored, as they are likely derived fields, and not part of the value of the Object. + *

+ * + *

+ * Static fields will not be tested. Superclass fields will be included. + *

+ * + *

+ * Two randomly chosen, non-zero, odd numbers must be passed in. Ideally these should be different for each class, + * however this is not vital. Prime numbers are preferred, especially for the multiplier. + *

+ * + * @param initialNonZeroOddNumber + * a non-zero, odd number used as the initial value + * @param multiplierNonZeroOddNumber + * a non-zero, odd number used as the multiplier + * @param object + * the Object to create a hashCode for + * @param testTransients + * whether to include transient fields + * @return int hash code + * @throws IllegalArgumentException + * if the Object is null + * @throws IllegalArgumentException + * if the number is zero or even + */ + public static int reflectionHashCode(int initialNonZeroOddNumber, int multiplierNonZeroOddNumber, Object object, + boolean testTransients) { + return reflectionHashCode(initialNonZeroOddNumber, multiplierNonZeroOddNumber, object, testTransients, null); + } + + /** + *

+ * This method uses reflection to build a valid hash code. + *

+ * + *

+ * It uses AccessibleObject.setAccessible to gain access to private fields. This means that it will + * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is + * also not as efficient as testing explicitly. + *

+ * + *

+ * If the TestTransients parameter is set to true, transient members will be tested, otherwise they + * are ignored, as they are likely derived fields, and not part of the value of the Object. + *

+ * + *

+ * Static fields will not be included. Superclass fields will be included up to and including the specified + * superclass. A null superclass is treated as java.lang.Object. + *

+ * + *

+ * Two randomly chosen, non-zero, odd numbers must be passed in. Ideally these should be different for each class, + * however this is not vital. Prime numbers are preferred, especially for the multiplier. + *

+ * + * @param + * the type of the object involved + * @param initialNonZeroOddNumber + * a non-zero, odd number used as the initial value + * @param multiplierNonZeroOddNumber + * a non-zero, odd number used as the multiplier + * @param object + * the Object to create a hashCode for + * @param testTransients + * whether to include transient fields + * @param reflectUpToClass + * the superclass to reflect up to (inclusive), may be null + * @param excludeFields + * array of field names to exclude from use in calculation of hash code + * @return int hash code + * @throws IllegalArgumentException + * if the Object is null + * @throws IllegalArgumentException + * if the number is zero or even + * @since 2.0 + */ + public static int reflectionHashCode(int initialNonZeroOddNumber, int multiplierNonZeroOddNumber, T object, + boolean testTransients, Class reflectUpToClass, String... excludeFields) { + + if (object == null) { + throw new IllegalArgumentException("The object to build a hash code for must not be null"); + } + HashCodeBuilder builder = new HashCodeBuilder(initialNonZeroOddNumber, multiplierNonZeroOddNumber); + Class clazz = object.getClass(); + reflectionAppend(object, clazz, builder, testTransients, excludeFields); + while (clazz.getSuperclass() != null && clazz != reflectUpToClass) { + clazz = clazz.getSuperclass(); + reflectionAppend(object, clazz, builder, testTransients, excludeFields); + } + return builder.toHashCode(); + } + + /** + *

+ * This method uses reflection to build a valid hash code. + *

+ * + *

+ * This constructor uses two hard coded choices for the constants needed to build a hash code. + *

+ * + *

+ * It uses AccessibleObject.setAccessible to gain access to private fields. This means that it will + * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is + * also not as efficient as testing explicitly. + *

+ * + *

+ * If the TestTransients parameter is set to true, transient members will be tested, otherwise they + * are ignored, as they are likely derived fields, and not part of the value of the Object. + *

+ * + *

+ * Static fields will not be tested. Superclass fields will be included. + *

+ * + * @param object + * the Object to create a hashCode for + * @param testTransients + * whether to include transient fields + * @return int hash code + * @throws IllegalArgumentException + * if the object is null + */ + public static int reflectionHashCode(Object object, boolean testTransients) { + return reflectionHashCode(17, 37, object, testTransients, null); + } + + /** + *

+ * This method uses reflection to build a valid hash code. + *

+ * + *

+ * This constructor uses two hard coded choices for the constants needed to build a hash code. + *

+ * + *

+ * It uses AccessibleObject.setAccessible to gain access to private fields. This means that it will + * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is + * also not as efficient as testing explicitly. + *

+ * + *

+ * Transient members will be not be used, as they are likely derived fields, and not part of the value of the + * Object. + *

+ * + *

+ * Static fields will not be tested. Superclass fields will be included. + *

+ * + * @param object + * the Object to create a hashCode for + * @param excludeFields + * Collection of String field names to exclude from use in calculation of hash code + * @return int hash code + * @throws IllegalArgumentException + * if the object is null + */ + public static int reflectionHashCode(Object object, Collection excludeFields) { + return reflectionHashCode(object, ReflectionToStringBuilder.toNoNullStringArray(excludeFields)); + } + + // ------------------------------------------------------------------------- + + /** + *

+ * This method uses reflection to build a valid hash code. + *

+ * + *

+ * This constructor uses two hard coded choices for the constants needed to build a hash code. + *

+ * + *

+ * It uses AccessibleObject.setAccessible to gain access to private fields. This means that it will + * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is + * also not as efficient as testing explicitly. + *

+ * + *

+ * Transient members will be not be used, as they are likely derived fields, and not part of the value of the + * Object. + *

+ * + *

+ * Static fields will not be tested. Superclass fields will be included. + *

+ * + * @param object + * the Object to create a hashCode for + * @param excludeFields + * array of field names to exclude from use in calculation of hash code + * @return int hash code + * @throws IllegalArgumentException + * if the object is null + */ + public static int reflectionHashCode(Object object, String... excludeFields) { + return reflectionHashCode(17, 37, object, false, null, excludeFields); + } + + /** + *

+ * Registers the given object. Used by the reflection methods to avoid infinite loops. + *

+ * + * @param value + * The object to register. + */ + static void register(Object value) { + synchronized (HashCodeBuilder.class) { + if (getRegistry() == null) { + REGISTRY.set(new HashSet()); + } + } + getRegistry().add(new IDKey(value)); + } + + /** + *

+ * Unregisters the given object. + *

+ * + *

+ * Used by the reflection methods to avoid infinite loops. + * + * @param value + * The object to unregister. + * @since 2.3 + */ + static void unregister(Object value) { + Set registry = getRegistry(); + if (registry != null) { + registry.remove(new IDKey(value)); + synchronized (HashCodeBuilder.class) { + //read again + registry = getRegistry(); + if (registry != null && registry.isEmpty()) { + REGISTRY.remove(); + } + } + } + } + + /** + * Constant to use in building the hashCode. + */ + private final int iConstant; + + /** + * Running total of the hashCode. + */ + private int iTotal = 0; + + /** + *

+ * Uses two hard coded choices for the constants needed to build a hashCode. + *

+ */ + public HashCodeBuilder() { + iConstant = 37; + iTotal = 17; + } + + /** + *

+ * Two randomly chosen, non-zero, odd numbers must be passed in. Ideally these should be different for each class, + * however this is not vital. + *

+ * + *

+ * Prime numbers are preferred, especially for the multiplier. + *

+ * + * @param initialNonZeroOddNumber + * a non-zero, odd number used as the initial value + * @param multiplierNonZeroOddNumber + * a non-zero, odd number used as the multiplier + * @throws IllegalArgumentException + * if the number is zero or even + */ + public HashCodeBuilder(int initialNonZeroOddNumber, int multiplierNonZeroOddNumber) { + if (initialNonZeroOddNumber == 0) { + throw new IllegalArgumentException("HashCodeBuilder requires a non zero initial value"); + } + if (initialNonZeroOddNumber % 2 == 0) { + throw new IllegalArgumentException("HashCodeBuilder requires an odd initial value"); + } + if (multiplierNonZeroOddNumber == 0) { + throw new IllegalArgumentException("HashCodeBuilder requires a non zero multiplier"); + } + if (multiplierNonZeroOddNumber % 2 == 0) { + throw new IllegalArgumentException("HashCodeBuilder requires an odd multiplier"); + } + iConstant = multiplierNonZeroOddNumber; + iTotal = initialNonZeroOddNumber; + } + + /** + *

+ * Append a hashCode for a boolean. + *

+ *

+ * This adds 1 when true, and 0 when false to the hashCode. + *

+ *

+ * This is in contrast to the standard java.lang.Boolean.hashCode handling, which computes + * a hashCode value of 1231 for java.lang.Boolean instances + * that represent true or 1237 for java.lang.Boolean instances + * that represent false. + *

+ *

+ * This is in accordance with the Effective Java design. + *

+ * + * @param value + * the boolean to add to the hashCode + * @return this + */ + public HashCodeBuilder append(boolean value) { + iTotal = iTotal * iConstant + (value ? 0 : 1); + return this; + } + + /** + *

+ * Append a hashCode for a boolean array. + *

+ * + * @param array + * the array to add to the hashCode + * @return this + */ + public HashCodeBuilder append(boolean[] array) { + if (array == null) { + iTotal = iTotal * iConstant; + } else { + for (boolean element : array) { + append(element); + } + } + return this; + } + + // ------------------------------------------------------------------------- + + /** + *

+ * Append a hashCode for a byte. + *

+ * + * @param value + * the byte to add to the hashCode + * @return this + */ + public HashCodeBuilder append(byte value) { + iTotal = iTotal * iConstant + value; + return this; + } + + // ------------------------------------------------------------------------- + + /** + *

+ * Append a hashCode for a byte array. + *

+ * + * @param array + * the array to add to the hashCode + * @return this + */ + public HashCodeBuilder append(byte[] array) { + if (array == null) { + iTotal = iTotal * iConstant; + } else { + for (byte element : array) { + append(element); + } + } + return this; + } + + /** + *

+ * Append a hashCode for a char. + *

+ * + * @param value + * the char to add to the hashCode + * @return this + */ + public HashCodeBuilder append(char value) { + iTotal = iTotal * iConstant + value; + return this; + } + + /** + *

+ * Append a hashCode for a char array. + *

+ * + * @param array + * the array to add to the hashCode + * @return this + */ + public HashCodeBuilder append(char[] array) { + if (array == null) { + iTotal = iTotal * iConstant; + } else { + for (char element : array) { + append(element); + } + } + return this; + } + + /** + *

+ * Append a hashCode for a double. + *

+ * + * @param value + * the double to add to the hashCode + * @return this + */ + public HashCodeBuilder append(double value) { + return append(Double.doubleToLongBits(value)); + } + + /** + *

+ * Append a hashCode for a double array. + *

+ * + * @param array + * the array to add to the hashCode + * @return this + */ + public HashCodeBuilder append(double[] array) { + if (array == null) { + iTotal = iTotal * iConstant; + } else { + for (double element : array) { + append(element); + } + } + return this; + } + + /** + *

+ * Append a hashCode for a float. + *

+ * + * @param value + * the float to add to the hashCode + * @return this + */ + public HashCodeBuilder append(float value) { + iTotal = iTotal * iConstant + Float.floatToIntBits(value); + return this; + } + + /** + *

+ * Append a hashCode for a float array. + *

+ * + * @param array + * the array to add to the hashCode + * @return this + */ + public HashCodeBuilder append(float[] array) { + if (array == null) { + iTotal = iTotal * iConstant; + } else { + for (float element : array) { + append(element); + } + } + return this; + } + + /** + *

+ * Append a hashCode for an int. + *

+ * + * @param value + * the int to add to the hashCode + * @return this + */ + public HashCodeBuilder append(int value) { + iTotal = iTotal * iConstant + value; + return this; + } + + /** + *

+ * Append a hashCode for an int array. + *

+ * + * @param array + * the array to add to the hashCode + * @return this + */ + public HashCodeBuilder append(int[] array) { + if (array == null) { + iTotal = iTotal * iConstant; + } else { + for (int element : array) { + append(element); + } + } + return this; + } + + /** + *

+ * Append a hashCode for a long. + *

+ * + * @param value + * the long to add to the hashCode + * @return this + */ + // NOTE: This method uses >> and not >>> as Effective Java and + // Long.hashCode do. Ideally we should switch to >>> at + // some stage. There are backwards compat issues, so + // that will have to wait for the time being. cf LANG-342. + public HashCodeBuilder append(long value) { + iTotal = iTotal * iConstant + ((int) (value ^ (value >> 32))); + return this; + } + + /** + *

+ * Append a hashCode for a long array. + *

+ * + * @param array + * the array to add to the hashCode + * @return this + */ + public HashCodeBuilder append(long[] array) { + if (array == null) { + iTotal = iTotal * iConstant; + } else { + for (long element : array) { + append(element); + } + } + return this; + } + + /** + *

+ * Append a hashCode for an Object. + *

+ * + * @param object + * the Object to add to the hashCode + * @return this + */ + public HashCodeBuilder append(Object object) { + if (object == null) { + iTotal = iTotal * iConstant; + + } else { + if(object.getClass().isArray()) { + // 'Switch' on type of array, to dispatch to the correct handler + // This handles multi dimensional arrays + if (object instanceof long[]) { + append((long[]) object); + } else if (object instanceof int[]) { + append((int[]) object); + } else if (object instanceof short[]) { + append((short[]) object); + } else if (object instanceof char[]) { + append((char[]) object); + } else if (object instanceof byte[]) { + append((byte[]) object); + } else if (object instanceof double[]) { + append((double[]) object); + } else if (object instanceof float[]) { + append((float[]) object); + } else if (object instanceof boolean[]) { + append((boolean[]) object); + } else { + // Not an array of primitives + append((Object[]) object); + } + } else { + iTotal = iTotal * iConstant + object.hashCode(); + } + } + return this; + } + + /** + *

+ * Append a hashCode for an Object array. + *

+ * + * @param array + * the array to add to the hashCode + * @return this + */ + public HashCodeBuilder append(Object[] array) { + if (array == null) { + iTotal = iTotal * iConstant; + } else { + for (Object element : array) { + append(element); + } + } + return this; + } + + /** + *

+ * Append a hashCode for a short. + *

+ * + * @param value + * the short to add to the hashCode + * @return this + */ + public HashCodeBuilder append(short value) { + iTotal = iTotal * iConstant + value; + return this; + } + + /** + *

+ * Append a hashCode for a short array. + *

+ * + * @param array + * the array to add to the hashCode + * @return this + */ + public HashCodeBuilder append(short[] array) { + if (array == null) { + iTotal = iTotal * iConstant; + } else { + for (short element : array) { + append(element); + } + } + return this; + } + + /** + *

+ * Adds the result of super.hashCode() to this builder. + *

+ * + * @param superHashCode + * the result of calling super.hashCode() + * @return this HashCodeBuilder, used to chain calls. + * @since 2.0 + */ + public HashCodeBuilder appendSuper(int superHashCode) { + iTotal = iTotal * iConstant + superHashCode; + return this; + } + + /** + *

+ * Return the computed hashCode. + *

+ * + * @return hashCode based on the fields appended + */ + public int toHashCode() { + return iTotal; + } + + /** + * Returns the computed hashCode. + * + * @return hashCode based on the fields appended + * + * @since 3.0 + */ + @Override + public Integer build() { + return Integer.valueOf(toHashCode()); + } + + /** + *

+ * The computed hashCode from toHashCode() is returned due to the likelihood + * of bugs in mis-calling toHashCode() and the unlikeliness of it mattering what the hashCode for + * HashCodeBuilder itself is.

+ * + * @return hashCode based on the fields appended + * @since 2.5 + */ + @Override + public int hashCode() { + return toHashCode(); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/builder/HashCodeBuilderAndEqualsBuilderTest.java b/ApacheCommonsLang/org/apache/commons/lang3/builder/HashCodeBuilderAndEqualsBuilderTest.java new file mode 100644 index 0000000..c5948b7 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/builder/HashCodeBuilderAndEqualsBuilderTest.java @@ -0,0 +1,140 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.builder; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +/** + * Tests {@link org.apache.commons.lang3.builder.HashCodeBuilder} and + * {@link org.apache.commons.lang3.builder.EqualsBuilderTest} to insure that equal + * objects must have equal hash codes. + * + * @version $Id: HashCodeBuilderAndEqualsBuilderTest.java 1185707 2011-10-18 15:01:49Z ggregory $ + */ +public class HashCodeBuilderAndEqualsBuilderTest { + + //----------------------------------------------------------------------- + + private void testInteger(boolean testTransients) { + Integer i1 = Integer.valueOf(12345); + Integer i2 = Integer.valueOf(12345); + assertEqualsAndHashCodeContract(i1, i2, testTransients); + } + + @Test + public void testInteger() { + testInteger(false); + } + + @Test + public void testIntegerWithTransients() { + testInteger(true); + } + + @Test + public void testFixture() { + testFixture(false); + } + + @Test + public void testFixtureWithTransients() { + testFixture(true); + } + + private void testFixture(boolean testTransients) { + assertEqualsAndHashCodeContract(new TestFixture(2, 'c', "Test", (short) 2), new TestFixture(2, 'c', "Test", (short) 2), testTransients); + assertEqualsAndHashCodeContract( + new AllTransientFixture(2, 'c', "Test", (short) 2), + new AllTransientFixture(2, 'c', "Test", (short) 2), + testTransients); + assertEqualsAndHashCodeContract( + new SubTestFixture(2, 'c', "Test", (short) 2, "Same"), + new SubTestFixture(2, 'c', "Test", (short) 2, "Same"), + testTransients); + assertEqualsAndHashCodeContract( + new SubAllTransientFixture(2, 'c', "Test", (short) 2, "Same"), + new SubAllTransientFixture(2, 'c', "Test", (short) 2, "Same"), + testTransients); + } + + /** + * Asserts that if lhs equals rhs + * then their hash codes MUST be identical. + * + * @param lhs The Left-Hand-Side of the equals test + * @param rhs The Right-Hand-Side of the equals test + * @param testTransients wether to test transient fields + */ + private void assertEqualsAndHashCodeContract(Object lhs, Object rhs, boolean testTransients) { + if (EqualsBuilder.reflectionEquals(lhs, rhs, testTransients)) { + // test a couple of times for consistency. + assertEquals(HashCodeBuilder.reflectionHashCode(lhs, testTransients), HashCodeBuilder.reflectionHashCode(rhs, testTransients)); + assertEquals(HashCodeBuilder.reflectionHashCode(lhs, testTransients), HashCodeBuilder.reflectionHashCode(rhs, testTransients)); + assertEquals(HashCodeBuilder.reflectionHashCode(lhs, testTransients), HashCodeBuilder.reflectionHashCode(rhs, testTransients)); + } + } + + static class TestFixture { + int i; + char c; + String string; + short s; + + TestFixture(int i, char c, String string, short s) { + this.i = i; + this.c = c; + this.string = string; + this.s = s; + } + } + + static class SubTestFixture extends TestFixture { + transient String tString; + + SubTestFixture(int i, char c, String string, short s, String tString) { + super(i, c, string, s); + this.tString = tString; + } + } + + static class AllTransientFixture { + transient int i; + transient char c; + transient String string; + transient short s; + + AllTransientFixture(int i, char c, String string, short s) { + this.i = i; + this.c = c; + this.string = string; + this.s = s; + } + } + + static class SubAllTransientFixture extends AllTransientFixture { + transient String tString; + + SubAllTransientFixture(int i, char c, String string, short s, String tString) { + super(i, c, string, s); + this.tString = tString; + } + } + + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/builder/HashCodeBuilderTest.java b/ApacheCommonsLang/org/apache/commons/lang3/builder/HashCodeBuilderTest.java new file mode 100644 index 0000000..4b6765a --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/builder/HashCodeBuilderTest.java @@ -0,0 +1,547 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.lang3.builder; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import org.junit.Test; + +/** + * Unit tests {@link org.apache.commons.lang3.builder.HashCodeBuilder}. + * + * @version $Id: HashCodeBuilderTest.java 1344491 2012-05-30 22:48:11Z sebb $ + */ +public class HashCodeBuilderTest { + + /** + * A reflection test fixture. + */ + static class ReflectionTestCycleA { + ReflectionTestCycleB b; + + @Override + public int hashCode() { + return HashCodeBuilder.reflectionHashCode(this); + } + } + + /** + * A reflection test fixture. + */ + static class ReflectionTestCycleB { + ReflectionTestCycleA a; + + @Override + public int hashCode() { + return HashCodeBuilder.reflectionHashCode(this); + } + } + + // ----------------------------------------------------------------------- + + @Test(expected=IllegalArgumentException.class) + public void testConstructorEx1() { + new HashCodeBuilder(0, 0); + } + + @Test(expected=IllegalArgumentException.class) + public void testConstructorEx2() { + new HashCodeBuilder(2, 2); + } + + static class TestObject { + private int a; + + public TestObject(int a) { + this.a = a; + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (!(o instanceof TestObject)) { + return false; + } + TestObject rhs = (TestObject) o; + return a == rhs.a; + } + + public void setA(int a) { + this.a = a; + } + + public int getA() { + return a; + } + } + + static class TestSubObject extends TestObject { + private int b; + + @SuppressWarnings("unused") + transient private int t; + + public TestSubObject() { + super(0); + } + + public TestSubObject(int a, int b, int t) { + super(a); + this.b = b; + this.t = t; + } + + @Override + public boolean equals(Object o) { + if (o == this) { + return true; + } + if (!(o instanceof TestSubObject)) { + return false; + } + TestSubObject rhs = (TestSubObject) o; + return super.equals(o) && b == rhs.b; + } + } + + @Test + public void testReflectionHashCode() { + assertEquals(17 * 37, HashCodeBuilder.reflectionHashCode(new TestObject(0))); + assertEquals(17 * 37 + 123456, HashCodeBuilder.reflectionHashCode(new TestObject(123456))); + } + + @Test + public void testReflectionHierarchyHashCode() { + assertEquals(17 * 37 * 37, HashCodeBuilder.reflectionHashCode(new TestSubObject(0, 0, 0))); + assertEquals(17 * 37 * 37 * 37, HashCodeBuilder.reflectionHashCode(new TestSubObject(0, 0, 0), true)); + assertEquals((17 * 37 + 7890) * 37 + 123456, HashCodeBuilder.reflectionHashCode(new TestSubObject(123456, 7890, + 0))); + assertEquals(((17 * 37 + 7890) * 37 + 0) * 37 + 123456, HashCodeBuilder.reflectionHashCode(new TestSubObject( + 123456, 7890, 0), true)); + } + + @Test(expected=IllegalArgumentException.class) + public void testReflectionHierarchyHashCodeEx1() { + HashCodeBuilder.reflectionHashCode(0, 0, new TestSubObject(0, 0, 0), true); + } + + @Test(expected=IllegalArgumentException.class) + public void testReflectionHierarchyHashCodeEx2() { + HashCodeBuilder.reflectionHashCode(2, 2, new TestSubObject(0, 0, 0), true); + } + + @Test(expected=IllegalArgumentException.class) + public void testReflectionHashCodeEx1() { + HashCodeBuilder.reflectionHashCode(0, 0, new TestObject(0), true); + } + + @Test(expected=IllegalArgumentException.class) + public void testReflectionHashCodeEx2() { + HashCodeBuilder.reflectionHashCode(2, 2, new TestObject(0), true); + } + + @Test(expected=IllegalArgumentException.class) + public void testReflectionHashCodeEx3() { + HashCodeBuilder.reflectionHashCode(13, 19, null, true); + } + + @Test + public void testSuper() { + Object obj = new Object(); + assertEquals(17 * 37 + 19 * 41 + obj.hashCode(), new HashCodeBuilder(17, 37).appendSuper( + new HashCodeBuilder(19, 41).append(obj).toHashCode()).toHashCode()); + } + + @Test + public void testObject() { + Object obj = null; + assertEquals(17 * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode()); + obj = new Object(); + assertEquals(17 * 37 + obj.hashCode(), new HashCodeBuilder(17, 37).append(obj).toHashCode()); + } + + @Test + public void testObjectBuild() { + Object obj = null; + assertEquals(17 * 37, new HashCodeBuilder(17, 37).append(obj).build().intValue()); + obj = new Object(); + assertEquals(17 * 37 + obj.hashCode(), new HashCodeBuilder(17, 37).append(obj).build().intValue()); + } + + @Test + @SuppressWarnings("cast") // cast is not really needed, keep for consistency + public void testLong() { + assertEquals(17 * 37, new HashCodeBuilder(17, 37).append((long) 0L).toHashCode()); + assertEquals(17 * 37 + (int) (123456789L ^ 123456789L >> 32), new HashCodeBuilder(17, 37).append( + (long) 123456789L).toHashCode()); + } + + @Test + @SuppressWarnings("cast") // cast is not really needed, keep for consistency + public void testInt() { + assertEquals(17 * 37, new HashCodeBuilder(17, 37).append((int) 0).toHashCode()); + assertEquals(17 * 37 + 123456, new HashCodeBuilder(17, 37).append((int) 123456).toHashCode()); + } + + @Test + public void testShort() { + assertEquals(17 * 37, new HashCodeBuilder(17, 37).append((short) 0).toHashCode()); + assertEquals(17 * 37 + 12345, new HashCodeBuilder(17, 37).append((short) 12345).toHashCode()); + } + + @Test + public void testChar() { + assertEquals(17 * 37, new HashCodeBuilder(17, 37).append((char) 0).toHashCode()); + assertEquals(17 * 37 + 1234, new HashCodeBuilder(17, 37).append((char) 1234).toHashCode()); + } + + @Test + public void testByte() { + assertEquals(17 * 37, new HashCodeBuilder(17, 37).append((byte) 0).toHashCode()); + assertEquals(17 * 37 + 123, new HashCodeBuilder(17, 37).append((byte) 123).toHashCode()); + } + + @Test + @SuppressWarnings("cast") // cast is not really needed, keep for consistency + public void testDouble() { + assertEquals(17 * 37, new HashCodeBuilder(17, 37).append((double) 0d).toHashCode()); + double d = 1234567.89; + long l = Double.doubleToLongBits(d); + assertEquals(17 * 37 + (int) (l ^ l >> 32), new HashCodeBuilder(17, 37).append(d).toHashCode()); + } + + @Test + @SuppressWarnings("cast") // cast is not really needed, keep for consistency + public void testFloat() { + assertEquals(17 * 37, new HashCodeBuilder(17, 37).append((float) 0f).toHashCode()); + float f = 1234.89f; + int i = Float.floatToIntBits(f); + assertEquals(17 * 37 + i, new HashCodeBuilder(17, 37).append(f).toHashCode()); + } + + @Test + public void testBoolean() { + assertEquals(17 * 37 + 0, new HashCodeBuilder(17, 37).append(true).toHashCode()); + assertEquals(17 * 37 + 1, new HashCodeBuilder(17, 37).append(false).toHashCode()); + } + + @Test + public void testObjectArray() { + assertEquals(17 * 37, new HashCodeBuilder(17, 37).append((Object[]) null).toHashCode()); + Object[] obj = new Object[2]; + assertEquals(17 * 37 * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode()); + obj[0] = new Object(); + assertEquals((17 * 37 + obj[0].hashCode()) * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode()); + obj[1] = new Object(); + assertEquals((17 * 37 + obj[0].hashCode()) * 37 + obj[1].hashCode(), new HashCodeBuilder(17, 37).append(obj) + .toHashCode()); + } + + @Test + public void testObjectArrayAsObject() { + Object[] obj = new Object[2]; + assertEquals(17 * 37 * 37, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode()); + obj[0] = new Object(); + assertEquals((17 * 37 + obj[0].hashCode()) * 37, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode()); + obj[1] = new Object(); + assertEquals((17 * 37 + obj[0].hashCode()) * 37 + obj[1].hashCode(), new HashCodeBuilder(17, 37).append( + (Object) obj).toHashCode()); + } + + @Test + public void testLongArray() { + assertEquals(17 * 37, new HashCodeBuilder(17, 37).append((long[]) null).toHashCode()); + long[] obj = new long[2]; + assertEquals(17 * 37 * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode()); + obj[0] = 5L; + int h1 = (int) (5L ^ 5L >> 32); + assertEquals((17 * 37 + h1) * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode()); + obj[1] = 6L; + int h2 = (int) (6L ^ 6L >> 32); + assertEquals((17 * 37 + h1) * 37 + h2, new HashCodeBuilder(17, 37).append(obj).toHashCode()); + } + + @Test + public void testLongArrayAsObject() { + long[] obj = new long[2]; + assertEquals(17 * 37 * 37, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode()); + obj[0] = 5L; + int h1 = (int) (5L ^ 5L >> 32); + assertEquals((17 * 37 + h1) * 37, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode()); + obj[1] = 6L; + int h2 = (int) (6L ^ 6L >> 32); + assertEquals((17 * 37 + h1) * 37 + h2, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode()); + } + + @Test + public void testIntArray() { + assertEquals(17 * 37, new HashCodeBuilder(17, 37).append((int[]) null).toHashCode()); + int[] obj = new int[2]; + assertEquals(17 * 37 * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode()); + obj[0] = 5; + assertEquals((17 * 37 + 5) * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode()); + obj[1] = 6; + assertEquals((17 * 37 + 5) * 37 + 6, new HashCodeBuilder(17, 37).append(obj).toHashCode()); + } + + @Test + public void testIntArrayAsObject() { + int[] obj = new int[2]; + assertEquals(17 * 37 * 37, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode()); + obj[0] = 5; + assertEquals((17 * 37 + 5) * 37, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode()); + obj[1] = 6; + assertEquals((17 * 37 + 5) * 37 + 6, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode()); + } + + @Test + public void testShortArray() { + assertEquals(17 * 37, new HashCodeBuilder(17, 37).append((short[]) null).toHashCode()); + short[] obj = new short[2]; + assertEquals(17 * 37 * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode()); + obj[0] = (short) 5; + assertEquals((17 * 37 + 5) * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode()); + obj[1] = (short) 6; + assertEquals((17 * 37 + 5) * 37 + 6, new HashCodeBuilder(17, 37).append(obj).toHashCode()); + } + + @Test + public void testShortArrayAsObject() { + short[] obj = new short[2]; + assertEquals(17 * 37 * 37, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode()); + obj[0] = (short) 5; + assertEquals((17 * 37 + 5) * 37, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode()); + obj[1] = (short) 6; + assertEquals((17 * 37 + 5) * 37 + 6, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode()); + } + + @Test + public void testCharArray() { + assertEquals(17 * 37, new HashCodeBuilder(17, 37).append((char[]) null).toHashCode()); + char[] obj = new char[2]; + assertEquals(17 * 37 * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode()); + obj[0] = (char) 5; + assertEquals((17 * 37 + 5) * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode()); + obj[1] = (char) 6; + assertEquals((17 * 37 + 5) * 37 + 6, new HashCodeBuilder(17, 37).append(obj).toHashCode()); + } + + @Test + public void testCharArrayAsObject() { + char[] obj = new char[2]; + assertEquals(17 * 37 * 37, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode()); + obj[0] = (char) 5; + assertEquals((17 * 37 + 5) * 37, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode()); + obj[1] = (char) 6; + assertEquals((17 * 37 + 5) * 37 + 6, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode()); + } + + @Test + public void testByteArray() { + assertEquals(17 * 37, new HashCodeBuilder(17, 37).append((byte[]) null).toHashCode()); + byte[] obj = new byte[2]; + assertEquals(17 * 37 * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode()); + obj[0] = (byte) 5; + assertEquals((17 * 37 + 5) * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode()); + obj[1] = (byte) 6; + assertEquals((17 * 37 + 5) * 37 + 6, new HashCodeBuilder(17, 37).append(obj).toHashCode()); + } + + @Test + public void testByteArrayAsObject() { + byte[] obj = new byte[2]; + assertEquals(17 * 37 * 37, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode()); + obj[0] = (byte) 5; + assertEquals((17 * 37 + 5) * 37, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode()); + obj[1] = (byte) 6; + assertEquals((17 * 37 + 5) * 37 + 6, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode()); + } + + @Test + public void testDoubleArray() { + assertEquals(17 * 37, new HashCodeBuilder(17, 37).append((double[]) null).toHashCode()); + double[] obj = new double[2]; + assertEquals(17 * 37 * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode()); + obj[0] = 5.4d; + long l1 = Double.doubleToLongBits(5.4d); + int h1 = (int) (l1 ^ l1 >> 32); + assertEquals((17 * 37 + h1) * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode()); + obj[1] = 6.3d; + long l2 = Double.doubleToLongBits(6.3d); + int h2 = (int) (l2 ^ l2 >> 32); + assertEquals((17 * 37 + h1) * 37 + h2, new HashCodeBuilder(17, 37).append(obj).toHashCode()); + } + + @Test + public void testDoubleArrayAsObject() { + double[] obj = new double[2]; + assertEquals(17 * 37 * 37, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode()); + obj[0] = 5.4d; + long l1 = Double.doubleToLongBits(5.4d); + int h1 = (int) (l1 ^ l1 >> 32); + assertEquals((17 * 37 + h1) * 37, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode()); + obj[1] = 6.3d; + long l2 = Double.doubleToLongBits(6.3d); + int h2 = (int) (l2 ^ l2 >> 32); + assertEquals((17 * 37 + h1) * 37 + h2, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode()); + } + + @Test + public void testFloatArray() { + assertEquals(17 * 37, new HashCodeBuilder(17, 37).append((float[]) null).toHashCode()); + float[] obj = new float[2]; + assertEquals(17 * 37 * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode()); + obj[0] = 5.4f; + int h1 = Float.floatToIntBits(5.4f); + assertEquals((17 * 37 + h1) * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode()); + obj[1] = 6.3f; + int h2 = Float.floatToIntBits(6.3f); + assertEquals((17 * 37 + h1) * 37 + h2, new HashCodeBuilder(17, 37).append(obj).toHashCode()); + } + + @Test + public void testFloatArrayAsObject() { + float[] obj = new float[2]; + assertEquals(17 * 37 * 37, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode()); + obj[0] = 5.4f; + int h1 = Float.floatToIntBits(5.4f); + assertEquals((17 * 37 + h1) * 37, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode()); + obj[1] = 6.3f; + int h2 = Float.floatToIntBits(6.3f); + assertEquals((17 * 37 + h1) * 37 + h2, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode()); + } + + @Test + public void testBooleanArray() { + assertEquals(17 * 37, new HashCodeBuilder(17, 37).append((boolean[]) null).toHashCode()); + boolean[] obj = new boolean[2]; + assertEquals((17 * 37 + 1) * 37 + 1, new HashCodeBuilder(17, 37).append(obj).toHashCode()); + obj[0] = true; + assertEquals((17 * 37 + 0) * 37 + 1, new HashCodeBuilder(17, 37).append(obj).toHashCode()); + obj[1] = false; + assertEquals((17 * 37 + 0) * 37 + 1, new HashCodeBuilder(17, 37).append(obj).toHashCode()); + } + + @Test + public void testBooleanArrayAsObject() { + boolean[] obj = new boolean[2]; + assertEquals((17 * 37 + 1) * 37 + 1, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode()); + obj[0] = true; + assertEquals((17 * 37 + 0) * 37 + 1, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode()); + obj[1] = false; + assertEquals((17 * 37 + 0) * 37 + 1, new HashCodeBuilder(17, 37).append((Object) obj).toHashCode()); + } + + @Test + public void testBooleanMultiArray() { + boolean[][] obj = new boolean[2][]; + assertEquals(17 * 37 * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode()); + obj[0] = new boolean[0]; + assertEquals(17 * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode()); + obj[0] = new boolean[1]; + assertEquals((17 * 37 + 1) * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode()); + obj[0] = new boolean[2]; + assertEquals(((17 * 37 + 1) * 37 + 1) * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode()); + obj[0][0] = true; + assertEquals(((17 * 37 + 0) * 37 + 1) * 37, new HashCodeBuilder(17, 37).append(obj).toHashCode()); + obj[1] = new boolean[1]; + assertEquals(((17 * 37 + 0) * 37 + 1) * 37 + 1, new HashCodeBuilder(17, 37).append(obj).toHashCode()); + } + + @Test + public void testReflectionHashCodeExcludeFields() throws Exception { + TestObjectWithMultipleFields x = new TestObjectWithMultipleFields(1, 2, 3); + + assertEquals(((17 * 37 + 1) * 37 + 2) * 37 + 3, HashCodeBuilder.reflectionHashCode(x)); + + assertEquals(((17 * 37 + 1) * 37 + 2) * 37 + 3, HashCodeBuilder.reflectionHashCode(x, (String[]) null)); + assertEquals(((17 * 37 + 1) * 37 + 2) * 37 + 3, HashCodeBuilder.reflectionHashCode(x, new String[]{})); + assertEquals(((17 * 37 + 1) * 37 + 2) * 37 + 3, HashCodeBuilder.reflectionHashCode(x, new String[]{"xxx"})); + + assertEquals((17 * 37 + 1) * 37 + 3, HashCodeBuilder.reflectionHashCode(x, new String[]{"two"})); + assertEquals((17 * 37 + 1) * 37 + 2, HashCodeBuilder.reflectionHashCode(x, new String[]{"three"})); + + assertEquals(17 * 37 + 1, HashCodeBuilder.reflectionHashCode(x, new String[]{"two", "three"})); + + assertEquals(17, HashCodeBuilder.reflectionHashCode(x, new String[]{"one", "two", "three"})); + assertEquals(17, HashCodeBuilder.reflectionHashCode(x, new String[]{"one", "two", "three", "xxx"})); + } + + static class TestObjectWithMultipleFields { + @SuppressWarnings("unused") + private int one = 0; + + @SuppressWarnings("unused") + private int two = 0; + + @SuppressWarnings("unused") + private int three = 0; + + public TestObjectWithMultipleFields(int one, int two, int three) { + this.one = one; + this.two = two; + this.three = three; + } + } + + /** + * Test Objects pointing to each other. + */ + @Test + public void testReflectionObjectCycle() { + ReflectionTestCycleA a = new ReflectionTestCycleA(); + ReflectionTestCycleB b = new ReflectionTestCycleB(); + a.b = b; + b.a = a; + + // Used to caused: + // java.lang.StackOverflowError + // at java.lang.ClassLoader.getCallerClassLoader(Native Method) + // at java.lang.Class.getDeclaredFields(Class.java:992) + // at org.apache.commons.lang.builder.HashCodeBuilder.reflectionAppend(HashCodeBuilder.java:373) + // at org.apache.commons.lang.builder.HashCodeBuilder.reflectionHashCode(HashCodeBuilder.java:349) + // at org.apache.commons.lang.builder.HashCodeBuilder.reflectionHashCode(HashCodeBuilder.java:155) + // at + // org.apache.commons.lang.builder.HashCodeBuilderTest$ReflectionTestCycleB.hashCode(HashCodeBuilderTest.java:53) + // at org.apache.commons.lang.builder.HashCodeBuilder.append(HashCodeBuilder.java:422) + // at org.apache.commons.lang.builder.HashCodeBuilder.reflectionAppend(HashCodeBuilder.java:383) + // at org.apache.commons.lang.builder.HashCodeBuilder.reflectionHashCode(HashCodeBuilder.java:349) + // at org.apache.commons.lang.builder.HashCodeBuilder.reflectionHashCode(HashCodeBuilder.java:155) + // at + // org.apache.commons.lang.builder.HashCodeBuilderTest$ReflectionTestCycleA.hashCode(HashCodeBuilderTest.java:42) + // at org.apache.commons.lang.builder.HashCodeBuilder.append(HashCodeBuilder.java:422) + + a.hashCode(); + assertNull(HashCodeBuilder.getRegistry()); + b.hashCode(); + assertNull(HashCodeBuilder.getRegistry()); + } + + /** + * Ensures LANG-520 remains true + */ + @Test + public void testToHashCodeEqualsHashCode() { + HashCodeBuilder hcb = new HashCodeBuilder(17, 37).append(new Object()).append('a'); + assertEquals("hashCode() is no longer returning the same value as toHashCode() - see LANG-520", + hcb.toHashCode(), hcb.hashCode()); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/builder/IDKey.java b/ApacheCommonsLang/org/apache/commons/lang3/builder/IDKey.java new file mode 100644 index 0000000..68414c0 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/builder/IDKey.java @@ -0,0 +1,74 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.commons.lang3.builder; + +// adapted from org.apache.axis.utils.IDKey + +/** + * Wrap an identity key (System.identityHashCode()) + * so that an object can only be equal() to itself. + * + * This is necessary to disambiguate the occasional duplicate + * identityHashCodes that can occur. + * + */ +final class IDKey { + private final Object value; + private final int id; + + /** + * Constructor for IDKey + * @param _value The value + */ + public IDKey(Object _value) { + // This is the Object hashcode + id = System.identityHashCode(_value); + // There have been some cases (LANG-459) that return the + // same identity hash code for different objects. So + // the value is also added to disambiguate these cases. + value = _value; + } + + /** + * returns hashcode - i.e. the system identity hashcode. + * @return the hashcode + */ + @Override + public int hashCode() { + return id; + } + + /** + * checks if instances are equal + * @param other The other object to compare to + * @return if the instances are for the same object + */ + @Override + public boolean equals(Object other) { + if (!(other instanceof IDKey)) { + return false; + } + IDKey idKey = (IDKey) other; + if (id != idKey.id) { + return false; + } + // Note that identity equals is used. + return value == idKey.value; + } +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/builder/MultiLineToStringStyleTest.java b/ApacheCommonsLang/org/apache/commons/lang3/builder/MultiLineToStringStyleTest.java new file mode 100644 index 0000000..171d9c2 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/builder/MultiLineToStringStyleTest.java @@ -0,0 +1,132 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.builder; + +import static org.junit.Assert.assertEquals; + +import java.util.ArrayList; +import java.util.HashMap; + +import org.apache.commons.lang3.SystemUtils; +import org.apache.commons.lang3.builder.ToStringStyleTest.Person; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +/** + * Unit tests {@link org.apache.commons.lang3.builder.MultiLineToStringStyleTest}. + * + * @version $Id: MultiLineToStringStyleTest.java 1185704 2011-10-18 14:56:56Z ggregory $ + */ +public class MultiLineToStringStyleTest { + + private final Integer base = Integer.valueOf(5); + private final String baseStr = base.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(base)); + + @Before + public void setUp() throws Exception { + ToStringBuilder.setDefaultStyle(ToStringStyle.MULTI_LINE_STYLE); + } + + @After + public void tearDown() throws Exception { + ToStringBuilder.setDefaultStyle(ToStringStyle.DEFAULT_STYLE); + } + + //---------------------------------------------------------------- + + @Test + public void testBlank() { + assertEquals(baseStr + "[" + SystemUtils.LINE_SEPARATOR + "]", new ToStringBuilder(base).toString()); + } + + @Test + public void testAppendSuper() { + assertEquals(baseStr + "[" + SystemUtils.LINE_SEPARATOR + "]", new ToStringBuilder(base).appendSuper("Integer@8888[" + SystemUtils.LINE_SEPARATOR + "]").toString()); + assertEquals(baseStr + "[" + SystemUtils.LINE_SEPARATOR + " " + SystemUtils.LINE_SEPARATOR + "]", new ToStringBuilder(base).appendSuper("Integer@8888[" + SystemUtils.LINE_SEPARATOR + " " + SystemUtils.LINE_SEPARATOR + "]").toString()); + + assertEquals(baseStr + "[" + SystemUtils.LINE_SEPARATOR + " a=hello" + SystemUtils.LINE_SEPARATOR + "]", new ToStringBuilder(base).appendSuper("Integer@8888[" + SystemUtils.LINE_SEPARATOR + "]").append("a", "hello").toString()); + assertEquals(baseStr + "[" + SystemUtils.LINE_SEPARATOR + " " + SystemUtils.LINE_SEPARATOR + " a=hello" + SystemUtils.LINE_SEPARATOR + "]", new ToStringBuilder(base).appendSuper("Integer@8888[" + SystemUtils.LINE_SEPARATOR + " " + SystemUtils.LINE_SEPARATOR + "]").append("a", "hello").toString()); + assertEquals(baseStr + "[" + SystemUtils.LINE_SEPARATOR + " a=hello" + SystemUtils.LINE_SEPARATOR + "]", new ToStringBuilder(base).appendSuper(null).append("a", "hello").toString()); + } + + @Test + public void testObject() { + Integer i3 = Integer.valueOf(3); + Integer i4 = Integer.valueOf(4); + assertEquals(baseStr + "[" + SystemUtils.LINE_SEPARATOR + " " + SystemUtils.LINE_SEPARATOR + "]", new ToStringBuilder(base).append((Object) null).toString()); + assertEquals(baseStr + "[" + SystemUtils.LINE_SEPARATOR + " 3" + SystemUtils.LINE_SEPARATOR + "]", new ToStringBuilder(base).append(i3).toString()); + assertEquals(baseStr + "[" + SystemUtils.LINE_SEPARATOR + " a=" + SystemUtils.LINE_SEPARATOR + "]", new ToStringBuilder(base).append("a", (Object) null).toString()); + assertEquals(baseStr + "[" + SystemUtils.LINE_SEPARATOR + " a=3" + SystemUtils.LINE_SEPARATOR + "]", new ToStringBuilder(base).append("a", i3).toString()); + assertEquals(baseStr + "[" + SystemUtils.LINE_SEPARATOR + " a=3" + SystemUtils.LINE_SEPARATOR + " b=4" + SystemUtils.LINE_SEPARATOR + "]", new ToStringBuilder(base).append("a", i3).append("b", i4).toString()); + assertEquals(baseStr + "[" + SystemUtils.LINE_SEPARATOR + " a=" + SystemUtils.LINE_SEPARATOR + "]", new ToStringBuilder(base).append("a", i3, false).toString()); + assertEquals(baseStr + "[" + SystemUtils.LINE_SEPARATOR + " a=" + SystemUtils.LINE_SEPARATOR + "]", new ToStringBuilder(base).append("a", new ArrayList(), false).toString()); + assertEquals(baseStr + "[" + SystemUtils.LINE_SEPARATOR + " a=[]" + SystemUtils.LINE_SEPARATOR + "]", new ToStringBuilder(base).append("a", new ArrayList(), true).toString()); + assertEquals(baseStr + "[" + SystemUtils.LINE_SEPARATOR + " a=" + SystemUtils.LINE_SEPARATOR + "]", new ToStringBuilder(base).append("a", new HashMap(), false).toString()); + assertEquals(baseStr + "[" + SystemUtils.LINE_SEPARATOR + " a={}" + SystemUtils.LINE_SEPARATOR + "]", new ToStringBuilder(base).append("a", new HashMap(), true).toString()); + assertEquals(baseStr + "[" + SystemUtils.LINE_SEPARATOR + " a=" + SystemUtils.LINE_SEPARATOR + "]", new ToStringBuilder(base).append("a", (Object) new String[0], false).toString()); + assertEquals(baseStr + "[" + SystemUtils.LINE_SEPARATOR + " a={}" + SystemUtils.LINE_SEPARATOR + "]", new ToStringBuilder(base).append("a", (Object) new String[0], true).toString()); + } + + @Test + public void testPerson() { + Person p = new Person(); + p.name = "Jane Doe"; + p.age = 25; + p.smoker = true; + String pBaseStr = p.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(p)); + assertEquals(pBaseStr + "[" + SystemUtils.LINE_SEPARATOR + " name=Jane Doe" + SystemUtils.LINE_SEPARATOR + " age=25" + SystemUtils.LINE_SEPARATOR + " smoker=true" + SystemUtils.LINE_SEPARATOR + "]", new ToStringBuilder(p).append("name", p.name).append("age", p.age).append("smoker", p.smoker).toString()); + } + + @Test + public void testLong() { + assertEquals(baseStr + "[" + SystemUtils.LINE_SEPARATOR + " 3" + SystemUtils.LINE_SEPARATOR + "]", new ToStringBuilder(base).append(3L).toString()); + assertEquals(baseStr + "[" + SystemUtils.LINE_SEPARATOR + " a=3" + SystemUtils.LINE_SEPARATOR + "]", new ToStringBuilder(base).append("a", 3L).toString()); + assertEquals(baseStr + "[" + SystemUtils.LINE_SEPARATOR + " a=3" + SystemUtils.LINE_SEPARATOR + " b=4" + SystemUtils.LINE_SEPARATOR + "]", new ToStringBuilder(base).append("a", 3L).append("b", 4L).toString()); + } + + @Test + public void testObjectArray() { + Object[] array = new Object[] {null, base, new int[] {3, 6}}; + assertEquals(baseStr + "[" + SystemUtils.LINE_SEPARATOR + " {,5,{3,6}}" + SystemUtils.LINE_SEPARATOR + "]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[" + SystemUtils.LINE_SEPARATOR + " {,5,{3,6}}" + SystemUtils.LINE_SEPARATOR + "]", new ToStringBuilder(base).append((Object) array).toString()); + array = null; + assertEquals(baseStr + "[" + SystemUtils.LINE_SEPARATOR + " " + SystemUtils.LINE_SEPARATOR + "]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[" + SystemUtils.LINE_SEPARATOR + " " + SystemUtils.LINE_SEPARATOR + "]", new ToStringBuilder(base).append((Object) array).toString()); + } + + @Test + public void testLongArray() { + long[] array = new long[] {1, 2, -3, 4}; + assertEquals(baseStr + "[" + SystemUtils.LINE_SEPARATOR + " {1,2,-3,4}" + SystemUtils.LINE_SEPARATOR + "]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[" + SystemUtils.LINE_SEPARATOR + " {1,2,-3,4}" + SystemUtils.LINE_SEPARATOR + "]", new ToStringBuilder(base).append((Object) array).toString()); + array = null; + assertEquals(baseStr + "[" + SystemUtils.LINE_SEPARATOR + " " + SystemUtils.LINE_SEPARATOR + "]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[" + SystemUtils.LINE_SEPARATOR + " " + SystemUtils.LINE_SEPARATOR + "]", new ToStringBuilder(base).append((Object) array).toString()); + } + + @Test + public void testLongArrayArray() { + long[][] array = new long[][] {{1, 2}, null, {5}}; + assertEquals(baseStr + "[" + SystemUtils.LINE_SEPARATOR + " {{1,2},,{5}}" + SystemUtils.LINE_SEPARATOR + "]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[" + SystemUtils.LINE_SEPARATOR + " {{1,2},,{5}}" + SystemUtils.LINE_SEPARATOR + "]", new ToStringBuilder(base).append((Object) array).toString()); + array = null; + assertEquals(baseStr + "[" + SystemUtils.LINE_SEPARATOR + " " + SystemUtils.LINE_SEPARATOR + "]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[" + SystemUtils.LINE_SEPARATOR + " " + SystemUtils.LINE_SEPARATOR + "]", new ToStringBuilder(base).append((Object) array).toString()); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/builder/NoFieldNamesToStringStyleTest.java b/ApacheCommonsLang/org/apache/commons/lang3/builder/NoFieldNamesToStringStyleTest.java new file mode 100644 index 0000000..f6a9b30 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/builder/NoFieldNamesToStringStyleTest.java @@ -0,0 +1,131 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.builder; + +import static org.junit.Assert.assertEquals; + +import java.util.ArrayList; +import java.util.HashMap; + +import org.apache.commons.lang3.builder.ToStringStyleTest.Person; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +/** + * Unit tests {@link org.apache.commons.lang3.builder.NoFieldNamesToStringStyleTest}. + * + * @version $Id: NoFieldNamesToStringStyleTest.java 1185703 2011-10-18 14:55:38Z ggregory $ + */ +public class NoFieldNamesToStringStyleTest { + + private final Integer base = Integer.valueOf(5); + private final String baseStr = base.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(base)); + + @Before + public void setUp() throws Exception { + ToStringBuilder.setDefaultStyle(ToStringStyle.NO_FIELD_NAMES_STYLE); + } + + @After + public void tearDown() throws Exception { + ToStringBuilder.setDefaultStyle(ToStringStyle.DEFAULT_STYLE); + } + + //---------------------------------------------------------------- + + @Test + public void testBlank() { + assertEquals(baseStr + "[]", new ToStringBuilder(base).toString()); + } + + @Test + public void testAppendSuper() { + assertEquals(baseStr + "[]", new ToStringBuilder(base).appendSuper("Integer@8888[]").toString()); + assertEquals(baseStr + "[]", new ToStringBuilder(base).appendSuper("Integer@8888[]").toString()); + + assertEquals(baseStr + "[hello]", new ToStringBuilder(base).appendSuper("Integer@8888[]").append("a", "hello").toString()); + assertEquals(baseStr + "[,hello]", new ToStringBuilder(base).appendSuper("Integer@8888[]").append("a", "hello").toString()); + assertEquals(baseStr + "[hello]", new ToStringBuilder(base).appendSuper(null).append("a", "hello").toString()); + } + + @Test + public void testObject() { + Integer i3 = Integer.valueOf(3); + Integer i4 = Integer.valueOf(4); + assertEquals(baseStr + "[]", new ToStringBuilder(base).append((Object) null).toString()); + assertEquals(baseStr + "[3]", new ToStringBuilder(base).append(i3).toString()); + assertEquals(baseStr + "[]", new ToStringBuilder(base).append("a", (Object) null).toString()); + assertEquals(baseStr + "[3]", new ToStringBuilder(base).append("a", i3).toString()); + assertEquals(baseStr + "[3,4]", new ToStringBuilder(base).append("a", i3).append("b", i4).toString()); + assertEquals(baseStr + "[]", new ToStringBuilder(base).append("a", i3, false).toString()); + assertEquals(baseStr + "[]", new ToStringBuilder(base).append("a", new ArrayList(), false).toString()); + assertEquals(baseStr + "[[]]", new ToStringBuilder(base).append("a", new ArrayList(), true).toString()); + assertEquals(baseStr + "[]", new ToStringBuilder(base).append("a", new HashMap(), false).toString()); + assertEquals(baseStr + "[{}]", new ToStringBuilder(base).append("a", new HashMap(), true).toString()); + assertEquals(baseStr + "[]", new ToStringBuilder(base).append("a", (Object) new String[0], false).toString()); + assertEquals(baseStr + "[{}]", new ToStringBuilder(base).append("a", (Object) new String[0], true).toString()); + } + + @Test + public void testPerson() { + Person p = new Person(); + p.name = "Ron Paul"; + p.age = 72; + p.smoker = false; + String pBaseStr = p.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(p)); + assertEquals(pBaseStr + "[Ron Paul,72,false]", new ToStringBuilder(p).append("name", p.name).append("age", p.age).append("smoker", p.smoker).toString()); + } + + @Test + public void testLong() { + assertEquals(baseStr + "[3]", new ToStringBuilder(base).append(3L).toString()); + assertEquals(baseStr + "[3]", new ToStringBuilder(base).append("a", 3L).toString()); + assertEquals(baseStr + "[3,4]", new ToStringBuilder(base).append("a", 3L).append("b", 4L).toString()); + } + + @Test + public void testObjectArray() { + Object[] array = new Object[] {null, base, new int[] {3, 6}}; + assertEquals(baseStr + "[{,5,{3,6}}]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[{,5,{3,6}}]", new ToStringBuilder(base).append((Object) array).toString()); + array = null; + assertEquals(baseStr + "[]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[]", new ToStringBuilder(base).append((Object) array).toString()); + } + + @Test + public void testLongArray() { + long[] array = new long[] {1, 2, -3, 4}; + assertEquals(baseStr + "[{1,2,-3,4}]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[{1,2,-3,4}]", new ToStringBuilder(base).append((Object) array).toString()); + array = null; + assertEquals(baseStr + "[]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[]", new ToStringBuilder(base).append((Object) array).toString()); + } + + @Test + public void testLongArrayArray() { + long[][] array = new long[][] {{1, 2}, null, {5}}; + assertEquals(baseStr + "[{{1,2},,{5}}]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[{{1,2},,{5}}]", new ToStringBuilder(base).append((Object) array).toString()); + array = null; + assertEquals(baseStr + "[]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[]", new ToStringBuilder(base).append((Object) array).toString()); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/builder/ReflectionToStringBuilder.java b/ApacheCommonsLang/org/apache/commons/lang3/builder/ReflectionToStringBuilder.java new file mode 100644 index 0000000..89a78b8 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/builder/ReflectionToStringBuilder.java @@ -0,0 +1,691 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.lang3.builder; + +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.List; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.ClassUtils; + +/** + *

+ * Assists in implementing {@link Object#toString()} methods using reflection. + *

+ *

+ * This class uses reflection to determine the fields to append. Because these fields are usually private, the class + * uses {@link java.lang.reflect.AccessibleObject#setAccessible(java.lang.reflect.AccessibleObject[], boolean)} to + * change the visibility of the fields. This will fail under a security manager, unless the appropriate permissions are + * set up correctly. + *

+ *

+ * Using reflection to access (private) fields circumvents any synchronization protection guarding access to these + * fields. If a toString method cannot safely read a field, you should exclude it from the toString method, or use + * synchronization consistent with the class' lock management around the invocation of the method. Take special care to + * exclude non-thread-safe collection classes, because these classes may throw ConcurrentModificationException if + * modified while the toString method is executing. + *

+ *

+ * A typical invocation for this method would look like: + *

+ *
+ * public String toString() {
+ *     return ReflectionToStringBuilder.toString(this);
+ * }
+ * 
+ *

+ * You can also use the builder to debug 3rd party objects: + *

+ *
+ * System.out.println("An object: " + ReflectionToStringBuilder.toString(anObject));
+ * 
+ *

+ * A subclass can control field output by overriding the methods: + *

    + *
  • {@link #accept(java.lang.reflect.Field)}
  • + *
  • {@link #getValue(java.lang.reflect.Field)}
  • + *
+ *

+ *

+ * For example, this method does not include the password field in the returned String: + *

+ *
+ * public String toString() {
+ *     return (new ReflectionToStringBuilder(this) {
+ *         protected boolean accept(Field f) {
+ *             return super.accept(f) && !f.getName().equals("password");
+ *         }
+ *     }).toString();
+ * }
+ * 
+ *

+ * The exact format of the toString is determined by the {@link ToStringStyle} passed into the constructor. + *

+ * + * @since 2.0 + * @version $Id: ReflectionToStringBuilder.java 1200177 2011-11-10 06:14:33Z ggregory $ + */ +public class ReflectionToStringBuilder extends ToStringBuilder { + + /** + *

+ * Builds a toString value using the default ToStringStyle through reflection. + *

+ * + *

+ * It uses AccessibleObject.setAccessible to gain access to private fields. This means that it will + * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is + * also not as efficient as testing explicitly. + *

+ * + *

+ * Transient members will be not be included, as they are likely derived. Static fields will not be included. + * Superclass fields will be appended. + *

+ * + * @param object + * the Object to be output + * @return the String result + * @throws IllegalArgumentException + * if the Object is null + */ + public static String toString(Object object) { + return toString(object, null, false, false, null); + } + + /** + *

+ * Builds a toString value through reflection. + *

+ * + *

+ * It uses AccessibleObject.setAccessible to gain access to private fields. This means that it will + * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is + * also not as efficient as testing explicitly. + *

+ * + *

+ * Transient members will be not be included, as they are likely derived. Static fields will not be included. + * Superclass fields will be appended. + *

+ * + *

+ * If the style is null, the default ToStringStyle is used. + *

+ * + * @param object + * the Object to be output + * @param style + * the style of the toString to create, may be null + * @return the String result + * @throws IllegalArgumentException + * if the Object or ToStringStyle is null + */ + public static String toString(Object object, ToStringStyle style) { + return toString(object, style, false, false, null); + } + + /** + *

+ * Builds a toString value through reflection. + *

+ * + *

+ * It uses AccessibleObject.setAccessible to gain access to private fields. This means that it will + * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is + * also not as efficient as testing explicitly. + *

+ * + *

+ * If the outputTransients is true, transient members will be output, otherwise they + * are ignored, as they are likely derived fields, and not part of the value of the Object. + *

+ * + *

+ * Static fields will not be included. Superclass fields will be appended. + *

+ * + *

+ * If the style is null, the default ToStringStyle is used. + *

+ * + * @param object + * the Object to be output + * @param style + * the style of the toString to create, may be null + * @param outputTransients + * whether to include transient fields + * @return the String result + * @throws IllegalArgumentException + * if the Object is null + */ + public static String toString(Object object, ToStringStyle style, boolean outputTransients) { + return toString(object, style, outputTransients, false, null); + } + + /** + *

+ * Builds a toString value through reflection. + *

+ * + *

+ * It uses AccessibleObject.setAccessible to gain access to private fields. This means that it will + * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is + * also not as efficient as testing explicitly. + *

+ * + *

+ * If the outputTransients is true, transient fields will be output, otherwise they + * are ignored, as they are likely derived fields, and not part of the value of the Object. + *

+ * + *

+ * If the outputStatics is true, static fields will be output, otherwise they are + * ignored. + *

+ * + *

+ * Static fields will not be included. Superclass fields will be appended. + *

+ * + *

+ * If the style is null, the default ToStringStyle is used. + *

+ * + * @param object + * the Object to be output + * @param style + * the style of the toString to create, may be null + * @param outputTransients + * whether to include transient fields + * @param outputStatics + * whether to include transient fields + * @return the String result + * @throws IllegalArgumentException + * if the Object is null + * @since 2.1 + */ + public static String toString(Object object, ToStringStyle style, boolean outputTransients, boolean outputStatics) { + return toString(object, style, outputTransients, outputStatics, null); + } + + /** + *

+ * Builds a toString value through reflection. + *

+ * + *

+ * It uses AccessibleObject.setAccessible to gain access to private fields. This means that it will + * throw a security exception if run under a security manager, if the permissions are not set up correctly. It is + * also not as efficient as testing explicitly. + *

+ * + *

+ * If the outputTransients is true, transient fields will be output, otherwise they + * are ignored, as they are likely derived fields, and not part of the value of the Object. + *

+ * + *

+ * If the outputStatics is true, static fields will be output, otherwise they are + * ignored. + *

+ * + *

+ * Superclass fields will be appended up to and including the specified superclass. A null superclass is treated as + * java.lang.Object. + *

+ * + *

+ * If the style is null, the default ToStringStyle is used. + *

+ * + * @param + * the type of the object + * @param object + * the Object to be output + * @param style + * the style of the toString to create, may be null + * @param outputTransients + * whether to include transient fields + * @param outputStatics + * whether to include static fields + * @param reflectUpToClass + * the superclass to reflect up to (inclusive), may be null + * @return the String result + * @throws IllegalArgumentException + * if the Object is null + * @since 2.1 + */ + public static String toString( + T object, ToStringStyle style, boolean outputTransients, + boolean outputStatics, Class reflectUpToClass) { + return new ReflectionToStringBuilder(object, style, null, reflectUpToClass, outputTransients, outputStatics) + .toString(); + } + + /** + * Builds a String for a toString method excluding the given field names. + * + * @param object + * The object to "toString". + * @param excludeFieldNames + * The field names to exclude. Null excludes nothing. + * @return The toString value. + */ + public static String toStringExclude(Object object, Collection excludeFieldNames) { + return toStringExclude(object, toNoNullStringArray(excludeFieldNames)); + } + + /** + * Converts the given Collection into an array of Strings. The returned array does not contain null + * entries. Note that {@link Arrays#sort(Object[])} will throw an {@link NullPointerException} if an array element + * is null. + * + * @param collection + * The collection to convert + * @return A new array of Strings. + */ + static String[] toNoNullStringArray(Collection collection) { + if (collection == null) { + return ArrayUtils.EMPTY_STRING_ARRAY; + } + return toNoNullStringArray(collection.toArray()); + } + + /** + * Returns a new array of Strings without null elements. Internal method used to normalize exclude lists + * (arrays and collections). Note that {@link Arrays#sort(Object[])} will throw an {@link NullPointerException} + * if an array element is null. + * + * @param array + * The array to check + * @return The given array or a new array without null. + */ + static String[] toNoNullStringArray(Object[] array) { + List list = new ArrayList(array.length); + for (Object e : array) { + if (e != null) { + list.add(e.toString()); + } + } + return list.toArray(ArrayUtils.EMPTY_STRING_ARRAY); + } + + + /** + * Builds a String for a toString method excluding the given field names. + * + * @param object + * The object to "toString". + * @param excludeFieldNames + * The field names to exclude + * @return The toString value. + */ + public static String toStringExclude(Object object, String... excludeFieldNames) { + return new ReflectionToStringBuilder(object).setExcludeFieldNames(excludeFieldNames).toString(); + } + + /** + * Whether or not to append static fields. + */ + private boolean appendStatics = false; + + /** + * Whether or not to append transient fields. + */ + private boolean appendTransients = false; + + /** + * Which field names to exclude from output. Intended for fields like "password". + * + * @since 3.0 this is protected instead of private + */ + protected String[] excludeFieldNames; + + /** + * The last super class to stop appending fields for. + */ + private Class upToClass = null; + + /** + *

+ * Constructor. + *

+ * + *

+ * This constructor outputs using the default style set with setDefaultStyle. + *

+ * + * @param object + * the Object to build a toString for, must not be null + * @throws IllegalArgumentException + * if the Object passed in is null + */ + public ReflectionToStringBuilder(Object object) { + super(object); + } + + /** + *

+ * Constructor. + *

+ * + *

+ * If the style is null, the default style is used. + *

+ * + * @param object + * the Object to build a toString for, must not be null + * @param style + * the style of the toString to create, may be null + * @throws IllegalArgumentException + * if the Object passed in is null + */ + public ReflectionToStringBuilder(Object object, ToStringStyle style) { + super(object, style); + } + + /** + *

+ * Constructor. + *

+ * + *

+ * If the style is null, the default style is used. + *

+ * + *

+ * If the buffer is null, a new one is created. + *

+ * + * @param object + * the Object to build a toString for + * @param style + * the style of the toString to create, may be null + * @param buffer + * the StringBuffer to populate, may be null + * @throws IllegalArgumentException + * if the Object passed in is null + */ + public ReflectionToStringBuilder(Object object, ToStringStyle style, StringBuffer buffer) { + super(object, style, buffer); + } + + /** + * Constructor. + * + * @param + * the type of the object + * @param object + * the Object to build a toString for + * @param style + * the style of the toString to create, may be null + * @param buffer + * the StringBuffer to populate, may be null + * @param reflectUpToClass + * the superclass to reflect up to (inclusive), may be null + * @param outputTransients + * whether to include transient fields + * @param outputStatics + * whether to include static fields + * @since 2.1 + */ + public ReflectionToStringBuilder( + T object, ToStringStyle style, StringBuffer buffer, + Class reflectUpToClass, boolean outputTransients, boolean outputStatics) { + super(object, style, buffer); + this.setUpToClass(reflectUpToClass); + this.setAppendTransients(outputTransients); + this.setAppendStatics(outputStatics); + } + + /** + * Returns whether or not to append the given Field. + *
    + *
  • Transient fields are appended only if {@link #isAppendTransients()} returns true. + *
  • Static fields are appended only if {@link #isAppendStatics()} returns true. + *
  • Inner class fields are not appened.
  • + *
+ * + * @param field + * The Field to test. + * @return Whether or not to append the given Field. + */ + protected boolean accept(Field field) { + if (field.getName().indexOf(ClassUtils.INNER_CLASS_SEPARATOR_CHAR) != -1) { + // Reject field from inner class. + return false; + } + if (Modifier.isTransient(field.getModifiers()) && !this.isAppendTransients()) { + // Reject transient fields. + return false; + } + if (Modifier.isStatic(field.getModifiers()) && !this.isAppendStatics()) { + // Reject static fields. + return false; + } + if (this.excludeFieldNames != null + && Arrays.binarySearch(this.excludeFieldNames, field.getName()) >= 0) { + // Reject fields from the getExcludeFieldNames list. + return false; + } + return true; + } + + /** + *

+ * Appends the fields and values defined by the given object of the given Class. + *

+ * + *

+ * If a cycle is detected as an object is "toString()'ed", such an object is rendered as if + * Object.toString() had been called and not implemented by the object. + *

+ * + * @param clazz + * The class of object parameter + */ + protected void appendFieldsIn(Class clazz) { + if (clazz.isArray()) { + this.reflectionAppendArray(this.getObject()); + return; + } + Field[] fields = clazz.getDeclaredFields(); + AccessibleObject.setAccessible(fields, true); + for (Field field : fields) { + String fieldName = field.getName(); + if (this.accept(field)) { + try { + // Warning: Field.get(Object) creates wrappers objects + // for primitive types. + Object fieldValue = this.getValue(field); + this.append(fieldName, fieldValue); + } catch (IllegalAccessException ex) { + //this can't happen. Would get a Security exception + // instead + //throw a runtime exception in case the impossible + // happens. + throw new InternalError("Unexpected IllegalAccessException: " + ex.getMessage()); + } + } + } + } + + /** + * @return Returns the excludeFieldNames. + */ + public String[] getExcludeFieldNames() { + return this.excludeFieldNames.clone(); + } + + /** + *

+ * Gets the last super class to stop appending fields for. + *

+ * + * @return The last super class to stop appending fields for. + */ + public Class getUpToClass() { + return this.upToClass; + } + + /** + *

+ * Calls java.lang.reflect.Field.get(Object). + *

+ * + * @param field + * The Field to query. + * @return The Object from the given Field. + * + * @throws IllegalArgumentException + * see {@link java.lang.reflect.Field#get(Object)} + * @throws IllegalAccessException + * see {@link java.lang.reflect.Field#get(Object)} + * + * @see java.lang.reflect.Field#get(Object) + */ + protected Object getValue(Field field) throws IllegalArgumentException, IllegalAccessException { + return field.get(this.getObject()); + } + + /** + *

+ * Gets whether or not to append static fields. + *

+ * + * @return Whether or not to append static fields. + * @since 2.1 + */ + public boolean isAppendStatics() { + return this.appendStatics; + } + + /** + *

+ * Gets whether or not to append transient fields. + *

+ * + * @return Whether or not to append transient fields. + */ + public boolean isAppendTransients() { + return this.appendTransients; + } + + /** + *

+ * Append to the toString an Object array. + *

+ * + * @param array + * the array to add to the toString + * @return this + */ + public ReflectionToStringBuilder reflectionAppendArray(Object array) { + this.getStyle().reflectionAppendArrayDetail(this.getStringBuffer(), null, array); + return this; + } + + /** + *

+ * Sets whether or not to append static fields. + *

+ * + * @param appendStatics + * Whether or not to append static fields. + * @since 2.1 + */ + public void setAppendStatics(boolean appendStatics) { + this.appendStatics = appendStatics; + } + + /** + *

+ * Sets whether or not to append transient fields. + *

+ * + * @param appendTransients + * Whether or not to append transient fields. + */ + public void setAppendTransients(boolean appendTransients) { + this.appendTransients = appendTransients; + } + + /** + * Sets the field names to exclude. + * + * @param excludeFieldNamesParam + * The excludeFieldNames to excluding from toString or null. + * @return this + */ + public ReflectionToStringBuilder setExcludeFieldNames(String... excludeFieldNamesParam) { + if (excludeFieldNamesParam == null) { + this.excludeFieldNames = null; + } else { + //clone and remove nulls + this.excludeFieldNames = toNoNullStringArray(excludeFieldNamesParam); + Arrays.sort(this.excludeFieldNames); + } + return this; + } + + /** + *

+ * Sets the last super class to stop appending fields for. + *

+ * + * @param clazz + * The last super class to stop appending fields for. + */ + public void setUpToClass(Class clazz) { + if (clazz != null) { + Object object = getObject(); + if (object != null && clazz.isInstance(object) == false) { + throw new IllegalArgumentException("Specified class is not a superclass of the object"); + } + } + this.upToClass = clazz; + } + + /** + *

+ * Gets the String built by this builder. + *

+ * + * @return the built string + */ + @Override + public String toString() { + if (this.getObject() == null) { + return this.getStyle().getNullText(); + } + Class clazz = this.getObject().getClass(); + this.appendFieldsIn(clazz); + while (clazz.getSuperclass() != null && clazz != this.getUpToClass()) { + clazz = clazz.getSuperclass(); + this.appendFieldsIn(clazz); + } + return super.toString(); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/builder/ReflectionToStringBuilderConcurrencyTest.java b/ApacheCommonsLang/org/apache/commons/lang3/builder/ReflectionToStringBuilderConcurrencyTest.java new file mode 100644 index 0000000..3f067a0 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/builder/ReflectionToStringBuilderConcurrencyTest.java @@ -0,0 +1,120 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.lang3.builder; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +import junit.framework.Assert; + +import org.junit.Ignore; +import org.junit.Test; + +/** + * Tests concurrent access for {@link ReflectionToStringBuilder}. + *

+ * The {@link ToStringStyle} class includes a registry to avoid infinite loops for objects with circular references. We + * want to make sure that we do not get concurrency exceptions accessing this registry. + *

+ *

+ * The tests on the non-thread-safe collections do not pass. + *

+ * + * @see [LANG-762] Handle or document ReflectionToStringBuilder + * and ToStringBuilder for collections that are not thread safe + * @since 3.1 + * @version $Id: ReflectionToStringBuilderConcurrencyTest.java 1309977 2012-04-05 17:53:39Z ggregory $ + */ +public class ReflectionToStringBuilderConcurrencyTest { + + static class CollectionHolder> { + T collection; + + CollectionHolder(T collection) { + this.collection = collection; + } + } + + private static final int DATA_SIZE = 100000; + private static final int REPEAT = 100; + + @Test + @Ignore + public void testLinkedList() throws InterruptedException, ExecutionException { + this.testConcurrency(new CollectionHolder>(new LinkedList())); + } + + @Test + @Ignore + public void testArrayList() throws InterruptedException, ExecutionException { + this.testConcurrency(new CollectionHolder>(new ArrayList())); + } + + @Test + @Ignore + public void testCopyOnWriteArrayList() throws InterruptedException, ExecutionException { + this.testConcurrency(new CollectionHolder>(new CopyOnWriteArrayList())); + } + + private void testConcurrency(final CollectionHolder> holder) throws InterruptedException, + ExecutionException { + final List list = holder.collection; + // make a big array that takes a long time to toString() + for (int i = 0; i < DATA_SIZE; i++) { + list.add(Integer.valueOf(i)); + } + // Create a thread pool with two threads to cause the most contention on the underlying resource. + final ExecutorService threadPool = Executors.newFixedThreadPool(2); + // Consumes toStrings + Callable consumer = new Callable() { + @Override + public Integer call() { + for (int i = 0; i < REPEAT; i++) { + String s = ReflectionToStringBuilder.toString(holder); + Assert.assertNotNull(s); + } + return REPEAT; + } + }; + // Produces changes in the list + Callable producer = new Callable() { + @Override + public Integer call() { + for (int i = 0; i < DATA_SIZE; i++) { + list.remove(list.get(0)); + } + return REPEAT; + } + }; + Collection> tasks = new ArrayList>(); + tasks.add(consumer); + tasks.add(producer); + final List> futures = threadPool.invokeAll(tasks); + for (Future future : futures) { + Assert.assertEquals(REPEAT, future.get().intValue()); + } + } +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/builder/ReflectionToStringBuilderExcludeTest.java b/ApacheCommonsLang/org/apache/commons/lang3/builder/ReflectionToStringBuilderExcludeTest.java new file mode 100644 index 0000000..9f4c4a1 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/builder/ReflectionToStringBuilderExcludeTest.java @@ -0,0 +1,137 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.lang3.builder; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import junit.framework.Assert; + +import org.apache.commons.lang3.ArrayUtils; +import org.junit.Test; + +/** + * @version $Id: ReflectionToStringBuilderExcludeTest.java 1185702 2011-10-18 14:53:59Z ggregory $ + */ +public class ReflectionToStringBuilderExcludeTest { + + class TestFixture { + @SuppressWarnings("unused") + private String secretField = SECRET_VALUE; + + @SuppressWarnings("unused") + private String showField = NOT_SECRET_VALUE; + } + + private static final String NOT_SECRET_FIELD = "showField"; + + private static final String NOT_SECRET_VALUE = "Hello World!"; + + private static final String SECRET_FIELD = "secretField"; + + private static final String SECRET_VALUE = "secret value"; + + @Test + public void test_toStringExclude() { + String toString = ReflectionToStringBuilder.toStringExclude(new TestFixture(), SECRET_FIELD); + this.validateSecretFieldAbsent(toString); + } + + @Test + public void test_toStringExcludeArray() { + String toString = ReflectionToStringBuilder.toStringExclude(new TestFixture(), new String[]{SECRET_FIELD}); + this.validateSecretFieldAbsent(toString); + } + + @Test + public void test_toStringExcludeArrayWithNull() { + String toString = ReflectionToStringBuilder.toStringExclude(new TestFixture(), new String[]{null}); + this.validateSecretFieldPresent(toString); + } + + @Test + public void test_toStringExcludeArrayWithNulls() { + String toString = ReflectionToStringBuilder.toStringExclude(new TestFixture(), new String[]{null, null}); + this.validateSecretFieldPresent(toString); + } + + @Test + public void test_toStringExcludeCollection() { + List excludeList = new ArrayList(); + excludeList.add(SECRET_FIELD); + String toString = ReflectionToStringBuilder.toStringExclude(new TestFixture(), excludeList); + this.validateSecretFieldAbsent(toString); + } + + @Test + public void test_toStringExcludeCollectionWithNull() { + List excludeList = new ArrayList(); + excludeList.add(null); + String toString = ReflectionToStringBuilder.toStringExclude(new TestFixture(), excludeList); + this.validateSecretFieldPresent(toString); + } + + @Test + public void test_toStringExcludeCollectionWithNulls() { + List excludeList = new ArrayList(); + excludeList.add(null); + excludeList.add(null); + String toString = ReflectionToStringBuilder.toStringExclude(new TestFixture(), excludeList); + this.validateSecretFieldPresent(toString); + } + + @Test + public void test_toStringExcludeEmptyArray() { + String toString = ReflectionToStringBuilder.toStringExclude(new TestFixture(), ArrayUtils.EMPTY_STRING_ARRAY); + this.validateSecretFieldPresent(toString); + } + + @Test + public void test_toStringExcludeEmptyCollection() { + String toString = ReflectionToStringBuilder.toStringExclude(new TestFixture(), new ArrayList()); + this.validateSecretFieldPresent(toString); + } + + @Test + public void test_toStringExcludeNullArray() { + String toString = ReflectionToStringBuilder.toStringExclude(new TestFixture(), (String[]) null); + this.validateSecretFieldPresent(toString); + } + + @Test + public void test_toStringExcludeNullCollection() { + String toString = ReflectionToStringBuilder.toStringExclude(new TestFixture(), (Collection) null); + this.validateSecretFieldPresent(toString); + } + + private void validateNonSecretField(String toString) { + Assert.assertTrue(toString.indexOf(NOT_SECRET_FIELD) > ArrayUtils.INDEX_NOT_FOUND); + Assert.assertTrue(toString.indexOf(NOT_SECRET_VALUE) > ArrayUtils.INDEX_NOT_FOUND); + } + + private void validateSecretFieldAbsent(String toString) { + Assert.assertEquals(ArrayUtils.INDEX_NOT_FOUND, toString.indexOf(SECRET_VALUE)); + this.validateNonSecretField(toString); + } + + private void validateSecretFieldPresent(String toString) { + Assert.assertTrue(toString.indexOf(SECRET_VALUE) > 0); + this.validateNonSecretField(toString); + } +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/builder/ReflectionToStringBuilderMutateInspectConcurrencyTest.java b/ApacheCommonsLang/org/apache/commons/lang3/builder/ReflectionToStringBuilderMutateInspectConcurrencyTest.java new file mode 100644 index 0000000..66d89ae --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/builder/ReflectionToStringBuilderMutateInspectConcurrencyTest.java @@ -0,0 +1,109 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.lang3.builder; + +import java.util.LinkedList; +import java.util.Random; + +import org.junit.Ignore; +import org.junit.Test; + +/** + * Tests concurrent access for {@link ReflectionToStringBuilder}. + *

+ * The {@link ToStringStyle} class includes a registry to avoid infinite loops for objects with circular references. We + * want to make sure that we do not get concurrency exceptions accessing this registry. + *

+ * + * @see [LANG-762] Handle or document ReflectionToStringBuilder + * and ToStringBuilder for collections that are not thread safe + * @since 3.1 + * @version $Id$ + */ +public class ReflectionToStringBuilderMutateInspectConcurrencyTest { + + class TestFixture { + final private LinkedList listField = new LinkedList(); + final private Random random = new Random(); + private int N = 100; + + public TestFixture() { + synchronized (this) { + for (int i = 0; i < N; i++) { + listField.add(Integer.valueOf(i)); + } + } + } + + public synchronized void add() { + listField.add(Integer.valueOf(random.nextInt(N))); + } + + public synchronized void delete() { + listField.remove(Integer.valueOf(random.nextInt(N))); + } + } + + class MutatingClient implements Runnable { + final private TestFixture testFixture; + final private Random random = new Random(); + + public MutatingClient(TestFixture testFixture) { + this.testFixture = testFixture; + } + + @Override + public void run() { + if (random.nextBoolean()) { + testFixture.add(); + } else { + testFixture.delete(); + } + } + } + + class InspectingClient implements Runnable { + final private TestFixture testFixture; + final private Random random = new Random(); + + public InspectingClient(TestFixture testFixture) { + this.testFixture = testFixture; + } + + @Override + public void run() { + ReflectionToStringBuilder.toString(testFixture); + } + } + + @Test + @Ignore + public void testConcurrency() throws Exception { + TestFixture testFixture = new TestFixture(); + final int numMutators = 10; + final int numIterations = 10; + for (int i = 0; i < numIterations; i++) { + for (int j = 0; j < numMutators; j++) { + Thread t = new Thread(new MutatingClient(testFixture)); + t.start(); + Thread s = new Thread(new InspectingClient(testFixture)); + s.start(); + } + } + } +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/builder/ShortPrefixToStringStyleTest.java b/ApacheCommonsLang/org/apache/commons/lang3/builder/ShortPrefixToStringStyleTest.java new file mode 100644 index 0000000..03aeffa --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/builder/ShortPrefixToStringStyleTest.java @@ -0,0 +1,131 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.builder; + +import static org.junit.Assert.assertEquals; + +import java.util.ArrayList; +import java.util.HashMap; + +import org.apache.commons.lang3.builder.ToStringStyleTest.Person; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +/** + * Unit tests {@link org.apache.commons.lang3.builder.ToStringStyle#SHORT_PREFIX_STYLE}. + * + * @version $Id: ShortPrefixToStringStyleTest.java 1185700 2011-10-18 14:52:55Z ggregory $ + */ +public class ShortPrefixToStringStyleTest { + + private final Integer base = Integer.valueOf(5); + private final String baseStr = "Integer"; + + @Before + public void setUp() throws Exception { + ToStringBuilder.setDefaultStyle(ToStringStyle.SHORT_PREFIX_STYLE); + } + + @After + public void tearDown() throws Exception { + ToStringBuilder.setDefaultStyle(ToStringStyle.DEFAULT_STYLE); + } + + //---------------------------------------------------------------- + + @Test + public void testBlank() { + assertEquals(baseStr + "[]", new ToStringBuilder(base).toString()); + } + + @Test + public void testAppendSuper() { + assertEquals(baseStr + "[]", new ToStringBuilder(base).appendSuper("Integer@8888[]").toString()); + assertEquals(baseStr + "[]", new ToStringBuilder(base).appendSuper("Integer@8888[]").toString()); + + assertEquals(baseStr + "[a=hello]", new ToStringBuilder(base).appendSuper("Integer@8888[]").append("a", "hello").toString()); + assertEquals(baseStr + "[,a=hello]", new ToStringBuilder(base).appendSuper("Integer@8888[]").append("a", "hello").toString()); + assertEquals(baseStr + "[a=hello]", new ToStringBuilder(base).appendSuper(null).append("a", "hello").toString()); + } + + @Test + public void testObject() { + Integer i3 = Integer.valueOf(3); + Integer i4 = Integer.valueOf(4); + assertEquals(baseStr + "[]", new ToStringBuilder(base).append((Object) null).toString()); + assertEquals(baseStr + "[3]", new ToStringBuilder(base).append(i3).toString()); + assertEquals(baseStr + "[a=]", new ToStringBuilder(base).append("a", (Object) null).toString()); + assertEquals(baseStr + "[a=3]", new ToStringBuilder(base).append("a", i3).toString()); + assertEquals(baseStr + "[a=3,b=4]", new ToStringBuilder(base).append("a", i3).append("b", i4).toString()); + assertEquals(baseStr + "[a=]", new ToStringBuilder(base).append("a", i3, false).toString()); + assertEquals(baseStr + "[a=]", new ToStringBuilder(base).append("a", new ArrayList(), false).toString()); + assertEquals(baseStr + "[a=[]]", new ToStringBuilder(base).append("a", new ArrayList(), true).toString()); + assertEquals(baseStr + "[a=]", new ToStringBuilder(base).append("a", new HashMap(), false).toString()); + assertEquals(baseStr + "[a={}]", new ToStringBuilder(base).append("a", new HashMap(), true).toString()); + assertEquals(baseStr + "[a=]", new ToStringBuilder(base).append("a", (Object) new String[0], false).toString()); + assertEquals(baseStr + "[a={}]", new ToStringBuilder(base).append("a", (Object) new String[0], true).toString()); + } + + @Test + public void testPerson() { + Person p = new Person(); + p.name = "John Q. Public"; + p.age = 45; + p.smoker = true; + String pBaseStr = "ToStringStyleTest.Person"; + assertEquals(pBaseStr + "[name=John Q. Public,age=45,smoker=true]", new ToStringBuilder(p).append("name", p.name).append("age", p.age).append("smoker", p.smoker).toString()); + } + + @Test + public void testLong() { + assertEquals(baseStr + "[3]", new ToStringBuilder(base).append(3L).toString()); + assertEquals(baseStr + "[a=3]", new ToStringBuilder(base).append("a", 3L).toString()); + assertEquals(baseStr + "[a=3,b=4]", new ToStringBuilder(base).append("a", 3L).append("b", 4L).toString()); + } + + @Test + public void testObjectArray() { + Object[] array = new Object[] {null, base, new int[] {3, 6}}; + assertEquals(baseStr + "[{,5,{3,6}}]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[{,5,{3,6}}]", new ToStringBuilder(base).append((Object) array).toString()); + array = null; + assertEquals(baseStr + "[]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[]", new ToStringBuilder(base).append((Object) array).toString()); + } + + @Test + public void testLongArray() { + long[] array = new long[] {1, 2, -3, 4}; + assertEquals(baseStr + "[{1,2,-3,4}]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[{1,2,-3,4}]", new ToStringBuilder(base).append((Object) array).toString()); + array = null; + assertEquals(baseStr + "[]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[]", new ToStringBuilder(base).append((Object) array).toString()); + } + + @Test + public void testLongArrayArray() { + long[][] array = new long[][] {{1, 2}, null, {5}}; + assertEquals(baseStr + "[{{1,2},,{5}}]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[{{1,2},,{5}}]", new ToStringBuilder(base).append((Object) array).toString()); + array = null; + assertEquals(baseStr + "[]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[]", new ToStringBuilder(base).append((Object) array).toString()); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/builder/SimpleToStringStyleTest.java b/ApacheCommonsLang/org/apache/commons/lang3/builder/SimpleToStringStyleTest.java new file mode 100644 index 0000000..6d2ed5d --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/builder/SimpleToStringStyleTest.java @@ -0,0 +1,129 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.builder; + +import static org.junit.Assert.assertEquals; + +import java.util.ArrayList; +import java.util.HashMap; + +import org.apache.commons.lang3.builder.ToStringStyleTest.Person; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +/** + * Unit tests {@link org.apache.commons.lang3.builder.SimpleToStringStyleTest}. + * + * @version $Id: SimpleToStringStyleTest.java 1185699 2011-10-18 14:51:31Z ggregory $ + */ +public class SimpleToStringStyleTest { + + private final Integer base = Integer.valueOf(5); + + @Before + public void setUp() throws Exception { + ToStringBuilder.setDefaultStyle(ToStringStyle.SIMPLE_STYLE); + } + + @After + public void tearDown() throws Exception { + ToStringBuilder.setDefaultStyle(ToStringStyle.DEFAULT_STYLE); + } + + //---------------------------------------------------------------- + + @Test + public void testBlank() { + assertEquals("", new ToStringBuilder(base).toString()); + } + + @Test + public void testAppendSuper() { + assertEquals("", new ToStringBuilder(base).appendSuper("").toString()); + assertEquals("", new ToStringBuilder(base).appendSuper("").toString()); + + assertEquals("hello", new ToStringBuilder(base).appendSuper("").append("a", "hello").toString()); + assertEquals(",hello", new ToStringBuilder(base).appendSuper("").append("a", "hello").toString()); + assertEquals("hello", new ToStringBuilder(base).appendSuper(null).append("a", "hello").toString()); + } + + @Test + public void testObject() { + Integer i3 = Integer.valueOf(3); + Integer i4 = Integer.valueOf(4); + assertEquals("", new ToStringBuilder(base).append((Object) null).toString()); + assertEquals("3", new ToStringBuilder(base).append(i3).toString()); + assertEquals("", new ToStringBuilder(base).append("a", (Object) null).toString()); + assertEquals("3", new ToStringBuilder(base).append("a", i3).toString()); + assertEquals("3,4", new ToStringBuilder(base).append("a", i3).append("b", i4).toString()); + assertEquals("", new ToStringBuilder(base).append("a", i3, false).toString()); + assertEquals("", new ToStringBuilder(base).append("a", new ArrayList(), false).toString()); + assertEquals("[]", new ToStringBuilder(base).append("a", new ArrayList(), true).toString()); + assertEquals("", new ToStringBuilder(base).append("a", new HashMap(), false).toString()); + assertEquals("{}", new ToStringBuilder(base).append("a", new HashMap(), true).toString()); + assertEquals("", new ToStringBuilder(base).append("a", (Object) new String[0], false).toString()); + assertEquals("{}", new ToStringBuilder(base).append("a", (Object) new String[0], true).toString()); + } + + @Test + public void testPerson() { + Person p = new Person(); + p.name = "Jane Q. Public"; + p.age = 47; + p.smoker = false; + assertEquals("Jane Q. Public,47,false", new ToStringBuilder(p).append("name", p.name).append("age", p.age).append("smoker", p.smoker).toString()); + } + + @Test + public void testLong() { + assertEquals("3", new ToStringBuilder(base).append(3L).toString()); + assertEquals("3", new ToStringBuilder(base).append("a", 3L).toString()); + assertEquals("3,4", new ToStringBuilder(base).append("a", 3L).append("b", 4L).toString()); + } + + @Test + public void testObjectArray() { + Object[] array = new Object[] {null, base, new int[] {3, 6}}; + assertEquals("{,5,{3,6}}", new ToStringBuilder(base).append(array).toString()); + assertEquals("{,5,{3,6}}", new ToStringBuilder(base).append((Object) array).toString()); + array = null; + assertEquals("", new ToStringBuilder(base).append(array).toString()); + assertEquals("", new ToStringBuilder(base).append((Object) array).toString()); + } + + @Test + public void testLongArray() { + long[] array = new long[] {1, 2, -3, 4}; + assertEquals("{1,2,-3,4}", new ToStringBuilder(base).append(array).toString()); + assertEquals("{1,2,-3,4}", new ToStringBuilder(base).append((Object) array).toString()); + array = null; + assertEquals("", new ToStringBuilder(base).append(array).toString()); + assertEquals("", new ToStringBuilder(base).append((Object) array).toString()); + } + + @Test + public void testLongArrayArray() { + long[][] array = new long[][] {{1, 2}, null, {5}}; + assertEquals("{{1,2},,{5}}", new ToStringBuilder(base).append(array).toString()); + assertEquals("{{1,2},,{5}}", new ToStringBuilder(base).append((Object) array).toString()); + array = null; + assertEquals("", new ToStringBuilder(base).append(array).toString()); + assertEquals("", new ToStringBuilder(base).append((Object) array).toString()); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/builder/StandardToStringStyle.java b/ApacheCommonsLang/org/apache/commons/lang3/builder/StandardToStringStyle.java new file mode 100644 index 0000000..b58b154 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/builder/StandardToStringStyle.java @@ -0,0 +1,560 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.builder; + +/** + *

Works with {@link ToStringBuilder} to create a toString.

+ * + *

This class is intended to be used as a singleton. + * There is no need to instantiate a new style each time. + * Simply instantiate the class once, customize the values as required, and + * store the result in a public static final variable for the rest of the + * program to access.

+ * + * @since 1.0 + * @version $Id: StandardToStringStyle.java 1089740 2011-04-07 05:01:54Z bayard $ + */ +public class StandardToStringStyle extends ToStringStyle { + + /** + * Required for serialization support. + * + * @see java.io.Serializable + */ + private static final long serialVersionUID = 1L; + + /** + *

Constructor.

+ */ + public StandardToStringStyle() { + super(); + } + + //--------------------------------------------------------------------- + + /** + *

Gets whether to use the class name.

+ * + * @return the current useClassName flag + */ + @Override + public boolean isUseClassName() { // NOPMD as this is implementing the abstract class + return super.isUseClassName(); + } + + /** + *

Sets whether to use the class name.

+ * + * @param useClassName the new useClassName flag + */ + @Override + public void setUseClassName(boolean useClassName) { // NOPMD as this is implementing the abstract class + super.setUseClassName(useClassName); + } + + //--------------------------------------------------------------------- + + /** + *

Gets whether to output short or long class names.

+ * + * @return the current useShortClassName flag + * @since 2.0 + */ + @Override + public boolean isUseShortClassName() { // NOPMD as this is implementing the abstract class + return super.isUseShortClassName(); + } + + /** + *

Sets whether to output short or long class names.

+ * + * @param useShortClassName the new useShortClassName flag + * @since 2.0 + */ + @Override + public void setUseShortClassName(boolean useShortClassName) { // NOPMD as this is implementing the abstract class + super.setUseShortClassName(useShortClassName); + } + + //--------------------------------------------------------------------- + + /** + *

Gets whether to use the identity hash code.

+ * @return the current useIdentityHashCode flag + */ + @Override + public boolean isUseIdentityHashCode() { // NOPMD as this is implementing the abstract class + return super.isUseIdentityHashCode(); + } + + /** + *

Sets whether to use the identity hash code.

+ * + * @param useIdentityHashCode the new useIdentityHashCode flag + */ + @Override + public void setUseIdentityHashCode(boolean useIdentityHashCode) { // NOPMD as this is implementing the abstract class + super.setUseIdentityHashCode(useIdentityHashCode); + } + + //--------------------------------------------------------------------- + + /** + *

Gets whether to use the field names passed in.

+ * + * @return the current useFieldNames flag + */ + @Override + public boolean isUseFieldNames() { // NOPMD as this is implementing the abstract class + return super.isUseFieldNames(); + } + + /** + *

Sets whether to use the field names passed in.

+ * + * @param useFieldNames the new useFieldNames flag + */ + @Override + public void setUseFieldNames(boolean useFieldNames) { // NOPMD as this is implementing the abstract class + super.setUseFieldNames(useFieldNames); + } + + //--------------------------------------------------------------------- + + /** + *

Gets whether to use full detail when the caller doesn't + * specify.

+ * + * @return the current defaultFullDetail flag + */ + @Override + public boolean isDefaultFullDetail() { // NOPMD as this is implementing the abstract class + return super.isDefaultFullDetail(); + } + + /** + *

Sets whether to use full detail when the caller doesn't + * specify.

+ * + * @param defaultFullDetail the new defaultFullDetail flag + */ + @Override + public void setDefaultFullDetail(boolean defaultFullDetail) { // NOPMD as this is implementing the abstract class + super.setDefaultFullDetail(defaultFullDetail); + } + + //--------------------------------------------------------------------- + + /** + *

Gets whether to output array content detail.

+ * + * @return the current array content detail setting + */ + @Override + public boolean isArrayContentDetail() { // NOPMD as this is implementing the abstract class + return super.isArrayContentDetail(); + } + + /** + *

Sets whether to output array content detail.

+ * + * @param arrayContentDetail the new arrayContentDetail flag + */ + @Override + public void setArrayContentDetail(boolean arrayContentDetail) { // NOPMD as this is implementing the abstract class + super.setArrayContentDetail(arrayContentDetail); + } + + //--------------------------------------------------------------------- + + /** + *

Gets the array start text.

+ * + * @return the current array start text + */ + @Override + public String getArrayStart() { // NOPMD as this is implementing the abstract class + return super.getArrayStart(); + } + + /** + *

Sets the array start text.

+ * + *

null is accepted, but will be converted + * to an empty String.

+ * + * @param arrayStart the new array start text + */ + @Override + public void setArrayStart(String arrayStart) { // NOPMD as this is implementing the abstract class + super.setArrayStart(arrayStart); + } + + //--------------------------------------------------------------------- + + /** + *

Gets the array end text.

+ * + * @return the current array end text + */ + @Override + public String getArrayEnd() { // NOPMD as this is implementing the abstract class + return super.getArrayEnd(); + } + + /** + *

Sets the array end text.

+ * + *

null is accepted, but will be converted + * to an empty String.

+ * + * @param arrayEnd the new array end text + */ + @Override + public void setArrayEnd(String arrayEnd) { // NOPMD as this is implementing the abstract class + super.setArrayEnd(arrayEnd); + } + + //--------------------------------------------------------------------- + + /** + *

Gets the array separator text.

+ * + * @return the current array separator text + */ + @Override + public String getArraySeparator() { // NOPMD as this is implementing the abstract class + return super.getArraySeparator(); + } + + /** + *

Sets the array separator text.

+ * + *

null is accepted, but will be converted + * to an empty String.

+ * + * @param arraySeparator the new array separator text + */ + @Override + public void setArraySeparator(String arraySeparator) { // NOPMD as this is implementing the abstract class + super.setArraySeparator(arraySeparator); + } + + //--------------------------------------------------------------------- + + /** + *

Gets the content start text.

+ * + * @return the current content start text + */ + @Override + public String getContentStart() { // NOPMD as this is implementing the abstract class + return super.getContentStart(); + } + + /** + *

Sets the content start text.

+ * + *

null is accepted, but will be converted + * to an empty String.

+ * + * @param contentStart the new content start text + */ + @Override + public void setContentStart(String contentStart) { // NOPMD as this is implementing the abstract class + super.setContentStart(contentStart); + } + + //--------------------------------------------------------------------- + + /** + *

Gets the content end text.

+ * + * @return the current content end text + */ + @Override + public String getContentEnd() { // NOPMD as this is implementing the abstract class + return super.getContentEnd(); + } + + /** + *

Sets the content end text.

+ * + *

null is accepted, but will be converted + * to an empty String.

+ * + * @param contentEnd the new content end text + */ + @Override + public void setContentEnd(String contentEnd) { // NOPMD as this is implementing the abstract class + super.setContentEnd(contentEnd); + } + + //--------------------------------------------------------------------- + + /** + *

Gets the field name value separator text.

+ * + * @return the current field name value separator text + */ + @Override + public String getFieldNameValueSeparator() { // NOPMD as this is implementing the abstract class + return super.getFieldNameValueSeparator(); + } + + /** + *

Sets the field name value separator text.

+ * + *

null is accepted, but will be converted + * to an empty String.

+ * + * @param fieldNameValueSeparator the new field name value separator text + */ + @Override + public void setFieldNameValueSeparator(String fieldNameValueSeparator) { // NOPMD as this is implementing the abstract class + super.setFieldNameValueSeparator(fieldNameValueSeparator); + } + + //--------------------------------------------------------------------- + + /** + *

Gets the field separator text.

+ * + * @return the current field separator text + */ + @Override + public String getFieldSeparator() { // NOPMD as this is implementing the abstract class + return super.getFieldSeparator(); + } + + /** + *

Sets the field separator text.

+ * + *

null is accepted, but will be converted + * to an empty String.

+ * + * @param fieldSeparator the new field separator text + */ + @Override + public void setFieldSeparator(String fieldSeparator) { // NOPMD as this is implementing the abstract class + super.setFieldSeparator(fieldSeparator); + } + + //--------------------------------------------------------------------- + + /** + *

Gets whether the field separator should be added at the start + * of each buffer.

+ * + * @return the fieldSeparatorAtStart flag + * @since 2.0 + */ + @Override + public boolean isFieldSeparatorAtStart() { // NOPMD as this is implementing the abstract class + return super.isFieldSeparatorAtStart(); + } + + /** + *

Sets whether the field separator should be added at the start + * of each buffer.

+ * + * @param fieldSeparatorAtStart the fieldSeparatorAtStart flag + * @since 2.0 + */ + @Override + public void setFieldSeparatorAtStart(boolean fieldSeparatorAtStart) { // NOPMD as this is implementing the abstract class + super.setFieldSeparatorAtStart(fieldSeparatorAtStart); + } + + //--------------------------------------------------------------------- + + /** + *

Gets whether the field separator should be added at the end + * of each buffer.

+ * + * @return fieldSeparatorAtEnd flag + * @since 2.0 + */ + @Override + public boolean isFieldSeparatorAtEnd() { // NOPMD as this is implementing the abstract class + return super.isFieldSeparatorAtEnd(); + } + + /** + *

Sets whether the field separator should be added at the end + * of each buffer.

+ * + * @param fieldSeparatorAtEnd the fieldSeparatorAtEnd flag + * @since 2.0 + */ + @Override + public void setFieldSeparatorAtEnd(boolean fieldSeparatorAtEnd) { // NOPMD as this is implementing the abstract class + super.setFieldSeparatorAtEnd(fieldSeparatorAtEnd); + } + + //--------------------------------------------------------------------- + + /** + *

Gets the text to output when null found.

+ * + * @return the current text to output when null found + */ + @Override + public String getNullText() { // NOPMD as this is implementing the abstract class + return super.getNullText(); + } + + /** + *

Sets the text to output when null found.

+ * + *

null is accepted, but will be converted + * to an empty String.

+ * + * @param nullText the new text to output when null found + */ + @Override + public void setNullText(String nullText) { // NOPMD as this is implementing the abstract class + super.setNullText(nullText); + } + + //--------------------------------------------------------------------- + + /** + *

Gets the text to output when a Collection, + * Map or Array size is output.

+ * + *

This is output before the size value.

+ * + * @return the current start of size text + */ + @Override + public String getSizeStartText() { // NOPMD as this is implementing the abstract class + return super.getSizeStartText(); + } + + /** + *

Sets the start text to output when a Collection, + * Map or Array size is output.

+ * + *

This is output before the size value.

+ * + *

null is accepted, but will be converted to + * an empty String.

+ * + * @param sizeStartText the new start of size text + */ + @Override + public void setSizeStartText(String sizeStartText) { // NOPMD as this is implementing the abstract class + super.setSizeStartText(sizeStartText); + } + + //--------------------------------------------------------------------- + + /** + * Gets the end text to output when a Collection, + * Map or Array size is output.

+ * + *

This is output after the size value.

+ * + * @return the current end of size text + */ + @Override + public String getSizeEndText() { // NOPMD as this is implementing the abstract class + return super.getSizeEndText(); + } + + /** + *

Sets the end text to output when a Collection, + * Map or Array size is output.

+ * + *

This is output after the size value.

+ * + *

null is accepted, but will be converted + * to an empty String.

+ * + * @param sizeEndText the new end of size text + */ + @Override + public void setSizeEndText(String sizeEndText) { // NOPMD as this is implementing the abstract class + super.setSizeEndText(sizeEndText); + } + + //--------------------------------------------------------------------- + + /** + *

Gets the start text to output when an Object is + * output in summary mode.

+ * + *

This is output before the size value.

+ * + * @return the current start of summary text + */ + @Override + public String getSummaryObjectStartText() { // NOPMD as this is implementing the abstract class + return super.getSummaryObjectStartText(); + } + + /** + *

Sets the start text to output when an Object is + * output in summary mode.

+ * + *

This is output before the size value.

+ * + *

null is accepted, but will be converted to + * an empty String.

+ * + * @param summaryObjectStartText the new start of summary text + */ + @Override + public void setSummaryObjectStartText(String summaryObjectStartText) { // NOPMD as this is implementing the abstract class + super.setSummaryObjectStartText(summaryObjectStartText); + } + + //--------------------------------------------------------------------- + + /** + *

Gets the end text to output when an Object is + * output in summary mode.

+ * + *

This is output after the size value.

+ * + * @return the current end of summary text + */ + @Override + public String getSummaryObjectEndText() { // NOPMD as this is implementing the abstract class + return super.getSummaryObjectEndText(); + } + + /** + *

Sets the end text to output when an Object is + * output in summary mode.

+ * + *

This is output after the size value.

+ * + *

null is accepted, but will be converted to + * an empty String.

+ * + * @param summaryObjectEndText the new end of summary text + */ + @Override + public void setSummaryObjectEndText(String summaryObjectEndText) { // NOPMD as this is implementing the abstract class + super.setSummaryObjectEndText(summaryObjectEndText); + } + + //--------------------------------------------------------------------- + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/builder/StandardToStringStyleTest.java b/ApacheCommonsLang/org/apache/commons/lang3/builder/StandardToStringStyleTest.java new file mode 100644 index 0000000..84f5d23 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/builder/StandardToStringStyleTest.java @@ -0,0 +1,146 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.builder; + +import static org.junit.Assert.assertEquals; + +import java.util.ArrayList; +import java.util.HashMap; + +import org.apache.commons.lang3.builder.ToStringStyleTest.Person; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +/** + * Unit tests {@link org.apache.commons.lang3.builder.ToStringStyle}. + * + * @version $Id: StandardToStringStyleTest.java 1185697 2011-10-18 14:50:15Z ggregory $ + */ +public class StandardToStringStyleTest { + + private final Integer base = Integer.valueOf(5); + private final String baseStr = "Integer"; + + private static final StandardToStringStyle STYLE = new StandardToStringStyle(); + + static { + STYLE.setUseShortClassName(true); + STYLE.setUseIdentityHashCode(false); + STYLE.setArrayStart("["); + STYLE.setArraySeparator(", "); + STYLE.setArrayEnd("]"); + STYLE.setNullText("%NULL%"); + STYLE.setSizeStartText("%SIZE="); + STYLE.setSizeEndText("%"); + STYLE.setSummaryObjectStartText("%"); + STYLE.setSummaryObjectEndText("%"); + } + + @Before + public void setUp() throws Exception { + ToStringBuilder.setDefaultStyle(STYLE); + } + + @After + public void tearDown() throws Exception { + ToStringBuilder.setDefaultStyle(ToStringStyle.DEFAULT_STYLE); + } + + //---------------------------------------------------------------- + + @Test + public void testBlank() { + assertEquals(baseStr + "[]", new ToStringBuilder(base).toString()); + } + + @Test + public void testAppendSuper() { + assertEquals(baseStr + "[]", new ToStringBuilder(base).appendSuper("Integer@8888[]").toString()); + assertEquals(baseStr + "[%NULL%]", new ToStringBuilder(base).appendSuper("Integer@8888[%NULL%]").toString()); + + assertEquals(baseStr + "[a=hello]", new ToStringBuilder(base).appendSuper("Integer@8888[]").append("a", "hello").toString()); + assertEquals(baseStr + "[%NULL%,a=hello]", new ToStringBuilder(base).appendSuper("Integer@8888[%NULL%]").append("a", "hello").toString()); + assertEquals(baseStr + "[a=hello]", new ToStringBuilder(base).appendSuper(null).append("a", "hello").toString()); + } + + @Test + public void testObject() { + Integer i3 = Integer.valueOf(3); + Integer i4 = Integer.valueOf(4); + assertEquals(baseStr + "[%NULL%]", new ToStringBuilder(base).append((Object) null).toString()); + assertEquals(baseStr + "[3]", new ToStringBuilder(base).append(i3).toString()); + assertEquals(baseStr + "[a=%NULL%]", new ToStringBuilder(base).append("a", (Object) null).toString()); + assertEquals(baseStr + "[a=3]", new ToStringBuilder(base).append("a", i3).toString()); + assertEquals(baseStr + "[a=3,b=4]", new ToStringBuilder(base).append("a", i3).append("b", i4).toString()); + assertEquals(baseStr + "[a=%Integer%]", new ToStringBuilder(base).append("a", i3, false).toString()); + assertEquals(baseStr + "[a=%SIZE=0%]", new ToStringBuilder(base).append("a", new ArrayList(), false).toString()); + assertEquals(baseStr + "[a=[]]", new ToStringBuilder(base).append("a", new ArrayList(), true).toString()); + assertEquals(baseStr + "[a=%SIZE=0%]", new ToStringBuilder(base).append("a", new HashMap(), false).toString()); + assertEquals(baseStr + "[a={}]", new ToStringBuilder(base).append("a", new HashMap(), true).toString()); + assertEquals(baseStr + "[a=%SIZE=0%]", new ToStringBuilder(base).append("a", (Object) new String[0], false).toString()); + assertEquals(baseStr + "[a=[]]", new ToStringBuilder(base).append("a", (Object) new String[0], true).toString()); + } + + @Test + public void testPerson() { + Person p = new Person(); + p.name = "Suzy Queue"; + p.age = 19; + p.smoker = false; + String pBaseStr = "ToStringStyleTest.Person"; + assertEquals(pBaseStr + "[name=Suzy Queue,age=19,smoker=false]", new ToStringBuilder(p).append("name", p.name).append("age", p.age).append("smoker", p.smoker).toString()); + } + + @Test + public void testLong() { + assertEquals(baseStr + "[3]", new ToStringBuilder(base).append(3L).toString()); + assertEquals(baseStr + "[a=3]", new ToStringBuilder(base).append("a", 3L).toString()); + assertEquals(baseStr + "[a=3,b=4]", new ToStringBuilder(base).append("a", 3L).append("b", 4L).toString()); + } + + @Test + public void testObjectArray() { + Object[] array = new Object[] {null, base, new int[] {3, 6}}; + assertEquals(baseStr + "[[%NULL%, 5, [3, 6]]]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[[%NULL%, 5, [3, 6]]]", new ToStringBuilder(base).append((Object) array).toString()); + array = null; + assertEquals(baseStr + "[%NULL%]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[%NULL%]", new ToStringBuilder(base).append((Object) array).toString()); + } + + @Test + public void testLongArray() { + long[] array = new long[] {1, 2, -3, 4}; + assertEquals(baseStr + "[[1, 2, -3, 4]]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[[1, 2, -3, 4]]", new ToStringBuilder(base).append((Object) array).toString()); + array = null; + assertEquals(baseStr + "[%NULL%]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[%NULL%]", new ToStringBuilder(base).append((Object) array).toString()); + } + + @Test + public void testLongArrayArray() { + long[][] array = new long[][] {{1, 2}, null, {5}}; + assertEquals(baseStr + "[[[1, 2], %NULL%, [5]]]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[[[1, 2], %NULL%, [5]]]", new ToStringBuilder(base).append((Object) array).toString()); + array = null; + assertEquals(baseStr + "[%NULL%]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[%NULL%]", new ToStringBuilder(base).append((Object) array).toString()); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/builder/ToStringBuilder.java b/ApacheCommonsLang/org/apache/commons/lang3/builder/ToStringBuilder.java new file mode 100644 index 0000000..593eb8d --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/builder/ToStringBuilder.java @@ -0,0 +1,1080 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.builder; + +import org.apache.commons.lang3.ObjectUtils; + +/** + *

Assists in implementing {@link Object#toString()} methods.

+ * + *

This class enables a good and consistent toString() to be built for any + * class or object. This class aims to simplify the process by:

+ *
    + *
  • allowing field names
  • + *
  • handling all types consistently
  • + *
  • handling nulls consistently
  • + *
  • outputting arrays and multi-dimensional arrays
  • + *
  • enabling the detail level to be controlled for Objects and Collections
  • + *
  • handling class hierarchies
  • + *
+ * + *

To use this class write code as follows:

+ * + *
+ * public class Person {
+ *   String name;
+ *   int age;
+ *   boolean smoker;
+ *
+ *   ...
+ *
+ *   public String toString() {
+ *     return new ToStringBuilder(this).
+ *       append("name", name).
+ *       append("age", age).
+ *       append("smoker", smoker).
+ *       toString();
+ *   }
+ * }
+ * 
+ * + *

This will produce a toString of the format: + * Person@7f54[name=Stephen,age=29,smoker=false]

+ * + *

To add the superclass toString, use {@link #appendSuper}. + * To append the toString from an object that is delegated + * to (or any other object), use {@link #appendToString}.

+ * + *

Alternatively, there is a method that uses reflection to determine + * the fields to test. Because these fields are usually private, the method, + * reflectionToString, uses AccessibleObject.setAccessible to + * change the visibility of the fields. This will fail under a security manager, + * unless the appropriate permissions are set up correctly. It is also + * slower than testing explicitly.

+ * + *

A typical invocation for this method would look like:

+ * + *
+ * public String toString() {
+ *   return ToStringBuilder.reflectionToString(this);
+ * }
+ * 
+ * + *

You can also use the builder to debug 3rd party objects:

+ * + *
+ * System.out.println("An object: " + ToStringBuilder.reflectionToString(anObject));
+ * 
+ * + *

The exact format of the toString is determined by + * the {@link ToStringStyle} passed into the constructor.

+ * + * @since 1.0 + * @version $Id: ToStringBuilder.java 1309977 2012-04-05 17:53:39Z ggregory $ + */ +public class ToStringBuilder implements Builder { + + /** + * The default style of output to use, not null. + */ + private static volatile ToStringStyle defaultStyle = ToStringStyle.DEFAULT_STYLE; + + //---------------------------------------------------------------------------- + + /** + *

Gets the default ToStringStyle to use.

+ * + *

This method gets a singleton default value, typically for the whole JVM. + * Changing this default should generally only be done during application startup. + * It is recommended to pass a ToStringStyle to the constructor instead + * of using this global default.

+ * + *

This method can be used from multiple threads. + * Internally, a volatile variable is used to provide the guarantee + * that the latest value set using {@link #setDefaultStyle} is the value returned. + * It is strongly recommended that the default style is only changed during application startup.

+ * + *

One reason for changing the default could be to have a verbose style during + * development and a compact style in production.

+ * + * @return the default ToStringStyle, never null + */ + public static ToStringStyle getDefaultStyle() { + return defaultStyle; + } + + /** + *

Sets the default ToStringStyle to use.

+ * + *

This method sets a singleton default value, typically for the whole JVM. + * Changing this default should generally only be done during application startup. + * It is recommended to pass a ToStringStyle to the constructor instead + * of changing this global default.

+ * + *

This method is not intended for use from multiple threads. + * Internally, a volatile variable is used to provide the guarantee + * that the latest value set is the value returned from {@link #getDefaultStyle}.

+ * + * @param style the default ToStringStyle + * @throws IllegalArgumentException if the style is null + */ + public static void setDefaultStyle(ToStringStyle style) { + if (style == null) { + throw new IllegalArgumentException("The style must not be null"); + } + defaultStyle = style; + } + + //---------------------------------------------------------------------------- + /** + *

Uses ReflectionToStringBuilder to generate a + * toString for the specified object.

+ * + * @param object the Object to be output + * @return the String result + * @see ReflectionToStringBuilder#toString(Object) + */ + public static String reflectionToString(Object object) { + return ReflectionToStringBuilder.toString(object); + } + + /** + *

Uses ReflectionToStringBuilder to generate a + * toString for the specified object.

+ * + * @param object the Object to be output + * @param style the style of the toString to create, may be null + * @return the String result + * @see ReflectionToStringBuilder#toString(Object,ToStringStyle) + */ + public static String reflectionToString(Object object, ToStringStyle style) { + return ReflectionToStringBuilder.toString(object, style); + } + + /** + *

Uses ReflectionToStringBuilder to generate a + * toString for the specified object.

+ * + * @param object the Object to be output + * @param style the style of the toString to create, may be null + * @param outputTransients whether to include transient fields + * @return the String result + * @see ReflectionToStringBuilder#toString(Object,ToStringStyle,boolean) + */ + public static String reflectionToString(Object object, ToStringStyle style, boolean outputTransients) { + return ReflectionToStringBuilder.toString(object, style, outputTransients, false, null); + } + + /** + *

Uses ReflectionToStringBuilder to generate a + * toString for the specified object.

+ * + * @param the type of the object + * @param object the Object to be output + * @param style the style of the toString to create, may be null + * @param outputTransients whether to include transient fields + * @param reflectUpToClass the superclass to reflect up to (inclusive), may be null + * @return the String result + * @see ReflectionToStringBuilder#toString(Object,ToStringStyle,boolean,boolean,Class) + * @since 2.0 + */ + public static String reflectionToString( + T object, + ToStringStyle style, + boolean outputTransients, + Class reflectUpToClass) { + return ReflectionToStringBuilder.toString(object, style, outputTransients, false, reflectUpToClass); + } + + //---------------------------------------------------------------------------- + + /** + * Current toString buffer, not null. + */ + private final StringBuffer buffer; + /** + * The object being output, may be null. + */ + private final Object object; + /** + * The style of output to use, not null. + */ + private final ToStringStyle style; + + /** + *

Constructs a builder for the specified object using the default output style.

+ * + *

This default style is obtained from {@link #getDefaultStyle()}.

+ * + * @param object the Object to build a toString for, not recommended to be null + */ + public ToStringBuilder(Object object) { + this(object, null, null); + } + + /** + *

Constructs a builder for the specified object using the a defined output style.

+ * + *

If the style is null, the default style is used.

+ * + * @param object the Object to build a toString for, not recommended to be null + * @param style the style of the toString to create, null uses the default style + */ + public ToStringBuilder(Object object, ToStringStyle style) { + this(object, style, null); + } + + /** + *

Constructs a builder for the specified object.

+ * + *

If the style is null, the default style is used.

+ * + *

If the buffer is null, a new one is created.

+ * + * @param object the Object to build a toString for, not recommended to be null + * @param style the style of the toString to create, null uses the default style + * @param buffer the StringBuffer to populate, may be null + */ + public ToStringBuilder(Object object, ToStringStyle style, StringBuffer buffer) { + if (style == null) { + style = getDefaultStyle(); + } + if (buffer == null) { + buffer = new StringBuffer(512); + } + this.buffer = buffer; + this.style = style; + this.object = object; + + style.appendStart(buffer, object); + } + + //---------------------------------------------------------------------------- + + /** + *

Append to the toString a boolean + * value.

+ * + * @param value the value to add to the toString + * @return this + */ + public ToStringBuilder append(boolean value) { + style.append(buffer, null, value); + return this; + } + + //---------------------------------------------------------------------------- + + /** + *

Append to the toString a boolean + * array.

+ * + * @param array the array to add to the toString + * @return this + */ + public ToStringBuilder append(boolean[] array) { + style.append(buffer, null, array, null); + return this; + } + + //---------------------------------------------------------------------------- + + /** + *

Append to the toString a byte + * value.

+ * + * @param value the value to add to the toString + * @return this + */ + public ToStringBuilder append(byte value) { + style.append(buffer, null, value); + return this; + } + + //---------------------------------------------------------------------------- + + /** + *

Append to the toString a byte + * array.

+ * + * @param array the array to add to the toString + * @return this + */ + public ToStringBuilder append(byte[] array) { + style.append(buffer, null, array, null); + return this; + } + + //---------------------------------------------------------------------------- + + /** + *

Append to the toString a char + * value.

+ * + * @param value the value to add to the toString + * @return this + */ + public ToStringBuilder append(char value) { + style.append(buffer, null, value); + return this; + } + + //---------------------------------------------------------------------------- + + /** + *

Append to the toString a char + * array.

+ * + * @param array the array to add to the toString + * @return this + */ + public ToStringBuilder append(char[] array) { + style.append(buffer, null, array, null); + return this; + } + + //---------------------------------------------------------------------------- + + /** + *

Append to the toString a double + * value.

+ * + * @param value the value to add to the toString + * @return this + */ + public ToStringBuilder append(double value) { + style.append(buffer, null, value); + return this; + } + + //---------------------------------------------------------------------------- + + /** + *

Append to the toString a double + * array.

+ * + * @param array the array to add to the toString + * @return this + */ + public ToStringBuilder append(double[] array) { + style.append(buffer, null, array, null); + return this; + } + + //---------------------------------------------------------------------------- + + /** + *

Append to the toString a float + * value.

+ * + * @param value the value to add to the toString + * @return this + */ + public ToStringBuilder append(float value) { + style.append(buffer, null, value); + return this; + } + + //---------------------------------------------------------------------------- + + /** + *

Append to the toString a float + * array.

+ * + * @param array the array to add to the toString + * @return this + */ + public ToStringBuilder append(float[] array) { + style.append(buffer, null, array, null); + return this; + } + + //---------------------------------------------------------------------------- + + /** + *

Append to the toString an int + * value.

+ * + * @param value the value to add to the toString + * @return this + */ + public ToStringBuilder append(int value) { + style.append(buffer, null, value); + return this; + } + + //---------------------------------------------------------------------------- + + /** + *

Append to the toString an int + * array.

+ * + * @param array the array to add to the toString + * @return this + */ + public ToStringBuilder append(int[] array) { + style.append(buffer, null, array, null); + return this; + } + + //---------------------------------------------------------------------------- + + /** + *

Append to the toString a long + * value.

+ * + * @param value the value to add to the toString + * @return this + */ + public ToStringBuilder append(long value) { + style.append(buffer, null, value); + return this; + } + + //---------------------------------------------------------------------------- + + /** + *

Append to the toString a long + * array.

+ * + * @param array the array to add to the toString + * @return this + */ + public ToStringBuilder append(long[] array) { + style.append(buffer, null, array, null); + return this; + } + + //---------------------------------------------------------------------------- + + /** + *

Append to the toString an Object + * value.

+ * + * @param obj the value to add to the toString + * @return this + */ + public ToStringBuilder append(Object obj) { + style.append(buffer, null, obj, null); + return this; + } + + //---------------------------------------------------------------------------- + + /** + *

Append to the toString an Object + * array.

+ * + * @param array the array to add to the toString + * @return this + */ + public ToStringBuilder append(Object[] array) { + style.append(buffer, null, array, null); + return this; + } + + //---------------------------------------------------------------------------- + + /** + *

Append to the toString a short + * value.

+ * + * @param value the value to add to the toString + * @return this + */ + public ToStringBuilder append(short value) { + style.append(buffer, null, value); + return this; + } + + //---------------------------------------------------------------------------- + + /** + *

Append to the toString a short + * array.

+ * + * @param array the array to add to the toString + * @return this + */ + public ToStringBuilder append(short[] array) { + style.append(buffer, null, array, null); + return this; + } + + /** + *

Append to the toString a boolean + * value.

+ * + * @param fieldName the field name + * @param value the value to add to the toString + * @return this + */ + public ToStringBuilder append(String fieldName, boolean value) { + style.append(buffer, fieldName, value); + return this; + } + + /** + *

Append to the toString a boolean + * array.

+ * + * @param fieldName the field name + * @param array the array to add to the hashCode + * @return this + */ + public ToStringBuilder append(String fieldName, boolean[] array) { + style.append(buffer, fieldName, array, null); + return this; + } + + /** + *

Append to the toString a boolean + * array.

+ * + *

A boolean parameter controls the level of detail to show. + * Setting true will output the array in full. Setting + * false will output a summary, typically the size of + * the array.

+ * + * @param fieldName the field name + * @param array the array to add to the toString + * @param fullDetail true for detail, false + * for summary info + * @return this + */ + public ToStringBuilder append(String fieldName, boolean[] array, boolean fullDetail) { + style.append(buffer, fieldName, array, Boolean.valueOf(fullDetail)); + return this; + } + + /** + *

Append to the toString an byte + * value.

+ * + * @param fieldName the field name + * @param value the value to add to the toString + * @return this + */ + public ToStringBuilder append(String fieldName, byte value) { + style.append(buffer, fieldName, value); + return this; + } + + /** + *

Append to the toString a byte array.

+ * + * @param fieldName the field name + * @param array the array to add to the toString + * @return this + */ + public ToStringBuilder append(String fieldName, byte[] array) { + style.append(buffer, fieldName, array, null); + return this; + } + + /** + *

Append to the toString a byte + * array.

+ * + *

A boolean parameter controls the level of detail to show. + * Setting true will output the array in full. Setting + * false will output a summary, typically the size of + * the array. + * + * @param fieldName the field name + * @param array the array to add to the toString + * @param fullDetail true for detail, false + * for summary info + * @return this + */ + public ToStringBuilder append(String fieldName, byte[] array, boolean fullDetail) { + style.append(buffer, fieldName, array, Boolean.valueOf(fullDetail)); + return this; + } + + /** + *

Append to the toString a char + * value.

+ * + * @param fieldName the field name + * @param value the value to add to the toString + * @return this + */ + public ToStringBuilder append(String fieldName, char value) { + style.append(buffer, fieldName, value); + return this; + } + + /** + *

Append to the toString a char + * array.

+ * + * @param fieldName the field name + * @param array the array to add to the toString + * @return this + */ + public ToStringBuilder append(String fieldName, char[] array) { + style.append(buffer, fieldName, array, null); + return this; + } + + /** + *

Append to the toString a char + * array.

+ * + *

A boolean parameter controls the level of detail to show. + * Setting true will output the array in full. Setting + * false will output a summary, typically the size of + * the array.

+ * + * @param fieldName the field name + * @param array the array to add to the toString + * @param fullDetail true for detail, false + * for summary info + * @return this + */ + public ToStringBuilder append(String fieldName, char[] array, boolean fullDetail) { + style.append(buffer, fieldName, array, Boolean.valueOf(fullDetail)); + return this; + } + + /** + *

Append to the toString a double + * value.

+ * + * @param fieldName the field name + * @param value the value to add to the toString + * @return this + */ + public ToStringBuilder append(String fieldName, double value) { + style.append(buffer, fieldName, value); + return this; + } + + /** + *

Append to the toString a double + * array.

+ * + * @param fieldName the field name + * @param array the array to add to the toString + * @return this + */ + public ToStringBuilder append(String fieldName, double[] array) { + style.append(buffer, fieldName, array, null); + return this; + } + + /** + *

Append to the toString a double + * array.

+ * + *

A boolean parameter controls the level of detail to show. + * Setting true will output the array in full. Setting + * false will output a summary, typically the size of + * the array.

+ * + * @param fieldName the field name + * @param array the array to add to the toString + * @param fullDetail true for detail, false + * for summary info + * @return this + */ + public ToStringBuilder append(String fieldName, double[] array, boolean fullDetail) { + style.append(buffer, fieldName, array, Boolean.valueOf(fullDetail)); + return this; + } + + /** + *

Append to the toString an float + * value.

+ * + * @param fieldName the field name + * @param value the value to add to the toString + * @return this + */ + public ToStringBuilder append(String fieldName, float value) { + style.append(buffer, fieldName, value); + return this; + } + + /** + *

Append to the toString a float + * array.

+ * + * @param fieldName the field name + * @param array the array to add to the toString + * @return this + */ + public ToStringBuilder append(String fieldName, float[] array) { + style.append(buffer, fieldName, array, null); + return this; + } + + /** + *

Append to the toString a float + * array.

+ * + *

A boolean parameter controls the level of detail to show. + * Setting true will output the array in full. Setting + * false will output a summary, typically the size of + * the array.

+ * + * @param fieldName the field name + * @param array the array to add to the toString + * @param fullDetail true for detail, false + * for summary info + * @return this + */ + public ToStringBuilder append(String fieldName, float[] array, boolean fullDetail) { + style.append(buffer, fieldName, array, Boolean.valueOf(fullDetail)); + return this; + } + + /** + *

Append to the toString an int + * value.

+ * + * @param fieldName the field name + * @param value the value to add to the toString + * @return this + */ + public ToStringBuilder append(String fieldName, int value) { + style.append(buffer, fieldName, value); + return this; + } + + /** + *

Append to the toString an int + * array.

+ * + * @param fieldName the field name + * @param array the array to add to the toString + * @return this + */ + public ToStringBuilder append(String fieldName, int[] array) { + style.append(buffer, fieldName, array, null); + return this; + } + + /** + *

Append to the toString an int + * array.

+ * + *

A boolean parameter controls the level of detail to show. + * Setting true will output the array in full. Setting + * false will output a summary, typically the size of + * the array.

+ * + * @param fieldName the field name + * @param array the array to add to the toString + * @param fullDetail true for detail, false + * for summary info + * @return this + */ + public ToStringBuilder append(String fieldName, int[] array, boolean fullDetail) { + style.append(buffer, fieldName, array, Boolean.valueOf(fullDetail)); + return this; + } + + /** + *

Append to the toString a long + * value.

+ * + * @param fieldName the field name + * @param value the value to add to the toString + * @return this + */ + public ToStringBuilder append(String fieldName, long value) { + style.append(buffer, fieldName, value); + return this; + } + + /** + *

Append to the toString a long + * array.

+ * + * @param fieldName the field name + * @param array the array to add to the toString + * @return this + */ + public ToStringBuilder append(String fieldName, long[] array) { + style.append(buffer, fieldName, array, null); + return this; + } + + /** + *

Append to the toString a long + * array.

+ * + *

A boolean parameter controls the level of detail to show. + * Setting true will output the array in full. Setting + * false will output a summary, typically the size of + * the array.

+ * + * @param fieldName the field name + * @param array the array to add to the toString + * @param fullDetail true for detail, false + * for summary info + * @return this + */ + public ToStringBuilder append(String fieldName, long[] array, boolean fullDetail) { + style.append(buffer, fieldName, array, Boolean.valueOf(fullDetail)); + return this; + } + + /** + *

Append to the toString an Object + * value.

+ * + * @param fieldName the field name + * @param obj the value to add to the toString + * @return this + */ + public ToStringBuilder append(String fieldName, Object obj) { + style.append(buffer, fieldName, obj, null); + return this; + } + + /** + *

Append to the toString an Object + * value.

+ * + * @param fieldName the field name + * @param obj the value to add to the toString + * @param fullDetail true for detail, + * false for summary info + * @return this + */ + public ToStringBuilder append(String fieldName, Object obj, boolean fullDetail) { + style.append(buffer, fieldName, obj, Boolean.valueOf(fullDetail)); + return this; + } + + /** + *

Append to the toString an Object + * array.

+ * + * @param fieldName the field name + * @param array the array to add to the toString + * @return this + */ + public ToStringBuilder append(String fieldName, Object[] array) { + style.append(buffer, fieldName, array, null); + return this; + } + + /** + *

Append to the toString an Object + * array.

+ * + *

A boolean parameter controls the level of detail to show. + * Setting true will output the array in full. Setting + * false will output a summary, typically the size of + * the array.

+ * + * @param fieldName the field name + * @param array the array to add to the toString + * @param fullDetail true for detail, false + * for summary info + * @return this + */ + public ToStringBuilder append(String fieldName, Object[] array, boolean fullDetail) { + style.append(buffer, fieldName, array, Boolean.valueOf(fullDetail)); + return this; + } + + /** + *

Append to the toString an short + * value.

+ * + * @param fieldName the field name + * @param value the value to add to the toString + * @return this + */ + public ToStringBuilder append(String fieldName, short value) { + style.append(buffer, fieldName, value); + return this; + } + + /** + *

Append to the toString a short + * array.

+ * + * @param fieldName the field name + * @param array the array to add to the toString + * @return this + */ + public ToStringBuilder append(String fieldName, short[] array) { + style.append(buffer, fieldName, array, null); + return this; + } + + /** + *

Append to the toString a short + * array.

+ * + *

A boolean parameter controls the level of detail to show. + * Setting true will output the array in full. Setting + * false will output a summary, typically the size of + * the array. + * + * @param fieldName the field name + * @param array the array to add to the toString + * @param fullDetail true for detail, false + * for summary info + * @return this + */ + public ToStringBuilder append(String fieldName, short[] array, boolean fullDetail) { + style.append(buffer, fieldName, array, Boolean.valueOf(fullDetail)); + return this; + } + + /** + *

Appends with the same format as the default Object toString() + * method. Appends the class name followed by + * {@link System#identityHashCode(java.lang.Object)}.

+ * + * @param object the Object whose class name and id to output + * @return this + * @since 2.0 + */ + public ToStringBuilder appendAsObjectToString(Object object) { + ObjectUtils.identityToString(this.getStringBuffer(), object); + return this; + } + + //---------------------------------------------------------------------------- + + /** + *

Append the toString from the superclass.

+ * + *

This method assumes that the superclass uses the same ToStringStyle + * as this one.

+ * + *

If superToString is null, no change is made.

+ * + * @param superToString the result of super.toString() + * @return this + * @since 2.0 + */ + public ToStringBuilder appendSuper(String superToString) { + if (superToString != null) { + style.appendSuper(buffer, superToString); + } + return this; + } + + /** + *

Append the toString from another object.

+ * + *

This method is useful where a class delegates most of the implementation of + * its properties to another class. You can then call toString() on + * the other class and pass the result into this method.

+ * + *
+     *   private AnotherObject delegate;
+     *   private String fieldInThisClass;
+     *
+     *   public String toString() {
+     *     return new ToStringBuilder(this).
+     *       appendToString(delegate.toString()).
+     *       append(fieldInThisClass).
+     *       toString();
+     *   }
+ * + *

This method assumes that the other object uses the same ToStringStyle + * as this one.

+ * + *

If the toString is null, no change is made.

+ * + * @param toString the result of toString() on another object + * @return this + * @since 2.0 + */ + public ToStringBuilder appendToString(String toString) { + if (toString != null) { + style.appendToString(buffer, toString); + } + return this; + } + + /** + *

Returns the Object being output.

+ * + * @return The object being output. + * @since 2.0 + */ + public Object getObject() { + return object; + } + + /** + *

Gets the StringBuffer being populated.

+ * + * @return the StringBuffer being populated + */ + public StringBuffer getStringBuffer() { + return buffer; + } + + //---------------------------------------------------------------------------- + + /** + *

Gets the ToStringStyle being used.

+ * + * @return the ToStringStyle being used + * @since 2.0 + */ + public ToStringStyle getStyle() { + return style; + } + + /** + *

Returns the built toString.

+ * + *

This method appends the end of data indicator, and can only be called once. + * Use {@link #getStringBuffer} to get the current string state.

+ * + *

If the object is null, return the style's nullText

+ * + * @return the String toString + */ + @Override + public String toString() { + if (this.getObject() == null) { + this.getStringBuffer().append(this.getStyle().getNullText()); + } else { + style.appendEnd(this.getStringBuffer(), this.getObject()); + } + return this.getStringBuffer().toString(); + } + + /** + * Returns the String that was build as an object representation. The + * default implementation utilizes the {@link #toString()} implementation. + * + * @return the String toString + * + * @see #toString() + * + * @since 3.0 + */ + @Override + public String build() { + return toString(); + } +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/builder/ToStringBuilderTest.java b/ApacheCommonsLang/org/apache/commons/lang3/builder/ToStringBuilderTest.java new file mode 100644 index 0000000..8036693 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/builder/ToStringBuilderTest.java @@ -0,0 +1,1086 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.builder; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.fail; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.commons.lang3.SystemUtils; +import org.junit.After; +import org.junit.Test; + +/** + * Unit tests for {@link org.apache.commons.lang3.builder.ToStringBuilder}. + * + * @version $Id: ToStringBuilderTest.java 1348417 2012-06-09 14:40:59Z sebb $ + */ +public class ToStringBuilderTest { + + private final Integer base = Integer.valueOf(5); + private final String baseStr = base.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(base)); + + /* + * All tests should leave the registry empty. + */ + @After + public void after(){ + validateNullToStringStyleRegistry(); + } + + //----------------------------------------------------------------------- + + @Test + public void testConstructorEx1() { + assertEquals("", new ToStringBuilder(null).toString()); + } + + @Test + public void testConstructorEx2() { + assertEquals("", new ToStringBuilder(null, null).toString()); + new ToStringBuilder(this.base, null).toString(); + } + + @Test + public void testConstructorEx3() { + assertEquals("", new ToStringBuilder(null, null, null).toString()); + new ToStringBuilder(this.base, null, null).toString(); + new ToStringBuilder(this.base, ToStringStyle.DEFAULT_STYLE, null).toString(); + } + + @Test + public void testGetSetDefault() { + try { + ToStringBuilder.setDefaultStyle(ToStringStyle.NO_FIELD_NAMES_STYLE); + assertSame(ToStringStyle.NO_FIELD_NAMES_STYLE, ToStringBuilder.getDefaultStyle()); + } finally { + // reset for other tests + ToStringBuilder.setDefaultStyle(ToStringStyle.DEFAULT_STYLE); + } + } + + @Test(expected=IllegalArgumentException.class) + public void testSetDefaultEx() { + ToStringBuilder.setDefaultStyle(null); + } + + @Test + public void testBlank() { + assertEquals(baseStr + "[]", new ToStringBuilder(base).toString()); + } + + /** + * Test wrapper for int primitive. + */ + @Test + public void testReflectionInteger() { + assertEquals(baseStr + "[value=5]", ToStringBuilder.reflectionToString(base)); + } + + /** + * Test wrapper for char primitive. + */ + @Test + public void testReflectionCharacter() { + Character c = new Character('A'); + assertEquals(this.toBaseString(c) + "[value=A]", ToStringBuilder.reflectionToString(c)); + } + + /** + * Test wrapper for char boolean. + */ + @Test + public void testReflectionBoolean() { + Boolean b; + b = Boolean.TRUE; + assertEquals(this.toBaseString(b) + "[value=true]", ToStringBuilder.reflectionToString(b)); + b = Boolean.FALSE; + assertEquals(this.toBaseString(b) + "[value=false]", ToStringBuilder.reflectionToString(b)); + } + + /** + * Create the same toString() as Object.toString(). + * @param o the object to create the string for. + * @return a String in the Object.toString format. + */ + private String toBaseString(Object o) { + return o.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(o)); + } + + // Reflection Array tests + + // + // Note on the following line of code repeated in the reflection array tests. + // + // assertReflectionArray("", array); + // + // The expected value is not baseStr + "[]" since array==null and is typed as Object. + // The null array does not carry array type information. + // If we added a primitive array type constructor and pile of associated methods, + // then type declaring type information could be carried forward. IMHO, null is null. + // + // Gary Gregory - 2003-03-12 - ggregory@seagullsw.com + // + + public void assertReflectionArray(String expected, Object actual) { + if (actual == null) { + // Until ToStringBuilder supports null objects. + return; + } + assertEquals(expected, ToStringBuilder.reflectionToString(actual)); + assertEquals(expected, ToStringBuilder.reflectionToString(actual, null)); + assertEquals(expected, ToStringBuilder.reflectionToString(actual, null, true)); + assertEquals(expected, ToStringBuilder.reflectionToString(actual, null, false)); + } + + @Test + public void testReflectionObjectArray() { + Object[] array = new Object[] { null, base, new int[] { 3, 6 } }; + String baseStr = this.toBaseString(array); + assertEquals(baseStr + "[{,5,{3,6}}]", ToStringBuilder.reflectionToString(array)); + array = null; + assertReflectionArray("", array); + } + + @Test + public void testReflectionLongArray() { + long[] array = new long[] { 1, 2, -3, 4 }; + String baseStr = this.toBaseString(array); + assertEquals(baseStr + "[{1,2,-3,4}]", ToStringBuilder.reflectionToString(array)); + array = null; + assertReflectionArray("", array); + } + + @Test + public void testReflectionIntArray() { + int[] array = new int[] { 1, 2, -3, 4 }; + String baseStr = this.toBaseString(array); + assertEquals(baseStr + "[{1,2,-3,4}]", ToStringBuilder.reflectionToString(array)); + array = null; + assertReflectionArray("", array); + } + + @Test + public void testReflectionShortArray() { + short[] array = new short[] { 1, 2, -3, 4 }; + String baseStr = this.toBaseString(array); + assertEquals(baseStr + "[{1,2,-3,4}]", ToStringBuilder.reflectionToString(array)); + array = null; + assertReflectionArray("", array); + } + + @Test + public void testReflectionyteArray() { + byte[] array = new byte[] { 1, 2, -3, 4 }; + String baseStr = this.toBaseString(array); + assertEquals(baseStr + "[{1,2,-3,4}]", ToStringBuilder.reflectionToString(array)); + array = null; + assertReflectionArray("", array); + } + + @Test + public void testReflectionCharArray() { + char[] array = new char[] { 'A', '2', '_', 'D' }; + String baseStr = this.toBaseString(array); + assertEquals(baseStr + "[{A,2,_,D}]", ToStringBuilder.reflectionToString(array)); + array = null; + assertReflectionArray("", array); + } + + @Test + public void testReflectionDoubleArray() { + double[] array = new double[] { 1.0, 2.9876, -3.00001, 4.3 }; + String baseStr = this.toBaseString(array); + assertEquals(baseStr + "[{1.0,2.9876,-3.00001,4.3}]", ToStringBuilder.reflectionToString(array)); + array = null; + assertReflectionArray("", array); + } + + @Test + public void testReflectionFloatArray() { + float[] array = new float[] { 1.0f, 2.9876f, -3.00001f, 4.3f }; + String baseStr = this.toBaseString(array); + assertEquals(baseStr + "[{1.0,2.9876,-3.00001,4.3}]", ToStringBuilder.reflectionToString(array)); + array = null; + assertReflectionArray("", array); + } + + @Test + public void testReflectionBooleanArray() { + boolean[] array = new boolean[] { true, false, false }; + String baseStr = this.toBaseString(array); + assertEquals(baseStr + "[{true,false,false}]", ToStringBuilder.reflectionToString(array)); + array = null; + assertReflectionArray("", array); + } + + // Reflection Array Array tests + + @Test + public void testReflectionFloatArrayArray() { + float[][] array = new float[][] { { 1.0f, 2.29686f }, null, { Float.NaN } }; + String baseStr = this.toBaseString(array); + assertEquals(baseStr + "[{{1.0,2.29686},,{NaN}}]", ToStringBuilder.reflectionToString(array)); + array = null; + assertReflectionArray("", array); + } + + + @Test + public void testReflectionLongArrayArray() { + long[][] array = new long[][] { { 1, 2 }, null, { 5 } }; + String baseStr = this.toBaseString(array); + assertEquals(baseStr + "[{{1,2},,{5}}]", ToStringBuilder.reflectionToString(array)); + array = null; + assertReflectionArray("", array); + } + + @Test + public void testReflectionIntArrayArray() { + int[][] array = new int[][] { { 1, 2 }, null, { 5 } }; + String baseStr = this.toBaseString(array); + assertEquals(baseStr + "[{{1,2},,{5}}]", ToStringBuilder.reflectionToString(array)); + array = null; + assertReflectionArray("", array); + } + + @Test + public void testReflectionhortArrayArray() { + short[][] array = new short[][] { { 1, 2 }, null, { 5 } }; + String baseStr = this.toBaseString(array); + assertEquals(baseStr + "[{{1,2},,{5}}]", ToStringBuilder.reflectionToString(array)); + array = null; + assertReflectionArray("", array); + } + + @Test + public void testReflectionByteArrayArray() { + byte[][] array = new byte[][] { { 1, 2 }, null, { 5 } }; + String baseStr = this.toBaseString(array); + assertEquals(baseStr + "[{{1,2},,{5}}]", ToStringBuilder.reflectionToString(array)); + array = null; + assertReflectionArray("", array); + } + + @Test + public void testReflectionCharArrayArray() { + char[][] array = new char[][] { { 'A', 'B' }, null, { 'p' } }; + String baseStr = this.toBaseString(array); + assertEquals(baseStr + "[{{A,B},,{p}}]", ToStringBuilder.reflectionToString(array)); + array = null; + assertReflectionArray("", array); + } + + @Test + public void testReflectionDoubleArrayArray() { + double[][] array = new double[][] { { 1.0, 2.29686 }, null, { Double.NaN } }; + String baseStr = this.toBaseString(array); + assertEquals(baseStr + "[{{1.0,2.29686},,{NaN}}]", ToStringBuilder.reflectionToString(array)); + array = null; + assertReflectionArray("", array); + } + + @Test + public void testReflectionBooleanArrayArray() { + boolean[][] array = new boolean[][] { { true, false }, null, { false } }; + String baseStr = this.toBaseString(array); + assertEquals(baseStr + "[{{true,false},,{false}}]", ToStringBuilder.reflectionToString(array)); + assertEquals(baseStr + "[{{true,false},,{false}}]", ToStringBuilder.reflectionToString(array)); + array = null; + assertReflectionArray("", array); + } + + // Reflection hierarchy tests + @Test + public void testReflectionHierarchyArrayList() { + List base = new ArrayList(); + String baseStr = this.toBaseString(base); + // note, the test data depends on the internal representation of the ArrayList, which may differ between JDK versions and vendors + String expectedWithTransients = baseStr + "[elementData={,,,,,,,,,},size=0,modCount=0]"; + String toStringWithTransients = ToStringBuilder.reflectionToString(base, null, true); + if (!expectedWithTransients.equals(toStringWithTransients)) { + // representation different for IBM JDK 1.6.0, LANG-727 + if (!("IBM Corporation".equals(SystemUtils.JAVA_VENDOR) && "1.6".equals(SystemUtils.JAVA_SPECIFICATION_VERSION))) { + assertEquals(expectedWithTransients, toStringWithTransients); + } + } + String expectedWithoutTransients = baseStr + "[size=0]"; + String toStringWithoutTransients = ToStringBuilder.reflectionToString(base, null, false); + if (!expectedWithoutTransients.equals(toStringWithoutTransients)) { + // representation different for IBM JDK 1.6.0, LANG-727 + if (!("IBM Corporation".equals(SystemUtils.JAVA_VENDOR) && "1.6".equals(SystemUtils.JAVA_SPECIFICATION_VERSION))) { + assertEquals(expectedWithoutTransients, toStringWithoutTransients); + } + } + } + + @Test + public void testReflectionHierarchy() { + ReflectionTestFixtureA baseA = new ReflectionTestFixtureA(); + String baseStr = this.toBaseString(baseA); + assertEquals(baseStr + "[a=a]", ToStringBuilder.reflectionToString(baseA)); + assertEquals(baseStr + "[a=a]", ToStringBuilder.reflectionToString(baseA, null)); + assertEquals(baseStr + "[a=a]", ToStringBuilder.reflectionToString(baseA, null, false)); + assertEquals(baseStr + "[a=a,transientA=t]", ToStringBuilder.reflectionToString(baseA, null, true)); + assertEquals(baseStr + "[a=a]", ToStringBuilder.reflectionToString(baseA, null, false, null)); + assertEquals(baseStr + "[a=a]", ToStringBuilder.reflectionToString(baseA, null, false, Object.class)); + assertEquals(baseStr + "[a=a]", ToStringBuilder.reflectionToString(baseA, null, false, ReflectionTestFixtureA.class)); + + ReflectionTestFixtureB baseB = new ReflectionTestFixtureB(); + baseStr = this.toBaseString(baseB); + assertEquals(baseStr + "[b=b,a=a]", ToStringBuilder.reflectionToString(baseB)); + assertEquals(baseStr + "[b=b,a=a]", ToStringBuilder.reflectionToString(baseB)); + assertEquals(baseStr + "[b=b,a=a]", ToStringBuilder.reflectionToString(baseB, null)); + assertEquals(baseStr + "[b=b,a=a]", ToStringBuilder.reflectionToString(baseB, null, false)); + assertEquals(baseStr + "[b=b,transientB=t,a=a,transientA=t]", ToStringBuilder.reflectionToString(baseB, null, true)); + assertEquals(baseStr + "[b=b,a=a]", ToStringBuilder.reflectionToString(baseB, null, false, null)); + assertEquals(baseStr + "[b=b,a=a]", ToStringBuilder.reflectionToString(baseB, null, false, Object.class)); + assertEquals(baseStr + "[b=b,a=a]", ToStringBuilder.reflectionToString(baseB, null, false, ReflectionTestFixtureA.class)); + assertEquals(baseStr + "[b=b]", ToStringBuilder.reflectionToString(baseB, null, false, ReflectionTestFixtureB.class)); + } + + static class ReflectionTestFixtureA { + @SuppressWarnings("unused") + private char a='a'; + @SuppressWarnings("unused") + private transient char transientA='t'; + } + + static class ReflectionTestFixtureB extends ReflectionTestFixtureA { + @SuppressWarnings("unused") + private char b='b'; + @SuppressWarnings("unused") + private transient char transientB='t'; + } + + @Test + public void testInnerClassReflection() { + Outer outer = new Outer(); + assertEquals(toBaseString(outer) + "[inner=" + toBaseString(outer.inner) + "[]]", outer.toString()); + } + + static class Outer { + Inner inner = new Inner(); + class Inner { + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this); + } + } + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this); + } + } + + // Reflection cycle tests + + /** + * Test an array element pointing to its container. + */ + @Test + public void testReflectionArrayCycle() throws Exception { + Object[] objects = new Object[1]; + objects[0] = objects; + assertEquals( + this.toBaseString(objects) + "[{" + this.toBaseString(objects) + "}]", + ToStringBuilder.reflectionToString(objects)); + } + + /** + * Test an array element pointing to its container. + */ + @Test + public void testReflectionArrayCycleLevel2() throws Exception { + Object[] objects = new Object[1]; + Object[] objectsLevel2 = new Object[1]; + objects[0] = objectsLevel2; + objectsLevel2[0] = objects; + assertEquals( + this.toBaseString(objects) + "[{{" + this.toBaseString(objects) + "}}]", + ToStringBuilder.reflectionToString(objects)); + assertEquals( + this.toBaseString(objectsLevel2) + "[{{" + this.toBaseString(objectsLevel2) + "}}]", + ToStringBuilder.reflectionToString(objectsLevel2)); + } + + @Test + public void testReflectionArrayArrayCycle() throws Exception { + Object[][] objects = new Object[2][2]; + objects[0][0] = objects; + objects[0][1] = objects; + objects[1][0] = objects; + objects[1][1] = objects; + String basicToString = this.toBaseString(objects); + assertEquals( + basicToString + + "[{{" + + basicToString + + "," + + basicToString + + "},{" + + basicToString + + "," + + basicToString + + "}}]", + ToStringBuilder.reflectionToString(objects)); + } + + /** + * A reflection test fixture. + */ + static class ReflectionTestCycleA { + ReflectionTestCycleB b; + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this); + } + } + + /** + * A reflection test fixture. + */ + static class ReflectionTestCycleB { + ReflectionTestCycleA a; + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this); + } + } + + /** + * A reflection test fixture. + */ + static class SimpleReflectionTestFixture { + Object o; + + public SimpleReflectionTestFixture() { + } + + public SimpleReflectionTestFixture(Object o) { + this.o = o; + } + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this); + } + } + + private static class SelfInstanceVarReflectionTestFixture { + @SuppressWarnings("unused") + private SelfInstanceVarReflectionTestFixture typeIsSelf; + + public SelfInstanceVarReflectionTestFixture() { + this.typeIsSelf = this; + } + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this); + } + } + + private static class SelfInstanceTwoVarsReflectionTestFixture { + @SuppressWarnings("unused") + private SelfInstanceTwoVarsReflectionTestFixture typeIsSelf; + private String otherType = "The Other Type"; + + public SelfInstanceTwoVarsReflectionTestFixture() { + this.typeIsSelf = this; + } + + public String getOtherType(){ + return this.otherType; + } + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this); + } + } + + + /** + * Test an Object pointing to itself, the simplest test. + * + * @throws Exception + */ + @Test + public void testSimpleReflectionObjectCycle() throws Exception { + SimpleReflectionTestFixture simple = new SimpleReflectionTestFixture(); + simple.o = simple; + assertEquals(this.toBaseString(simple) + "[o=" + this.toBaseString(simple) + "]", simple.toString()); + } + + /** + * Test a class that defines an ivar pointing to itself. + * + * @throws Exception + */ + @Test + public void testSelfInstanceVarReflectionObjectCycle() throws Exception { + SelfInstanceVarReflectionTestFixture test = new SelfInstanceVarReflectionTestFixture(); + assertEquals(this.toBaseString(test) + "[typeIsSelf=" + this.toBaseString(test) + "]", test.toString()); + } + + /** + * Test a class that defines an ivar pointing to itself. This test was + * created to show that handling cyclical object resulted in a missing endFieldSeparator call. + * + * @throws Exception + */ + @Test + public void testSelfInstanceTwoVarsReflectionObjectCycle() throws Exception { + SelfInstanceTwoVarsReflectionTestFixture test = new SelfInstanceTwoVarsReflectionTestFixture(); + assertEquals(this.toBaseString(test) + "[typeIsSelf=" + this.toBaseString(test) + ",otherType=" + test.getOtherType().toString() + "]", test.toString()); + } + + + /** + * Test Objects pointing to each other. + * + * @throws Exception + */ + @Test + public void testReflectionObjectCycle() throws Exception { + ReflectionTestCycleA a = new ReflectionTestCycleA(); + ReflectionTestCycleB b = new ReflectionTestCycleB(); + a.b = b; + b.a = a; + assertEquals( + this.toBaseString(a) + "[b=" + this.toBaseString(b) + "[a=" + this.toBaseString(a) + "]]", + a.toString()); + } + + /** + * Test a nasty combination of arrays and Objects pointing to each other. + * objects[0] -> SimpleReflectionTestFixture[ o -> objects ] + * + * @throws Exception + */ + @Test + public void testReflectionArrayAndObjectCycle() throws Exception { + Object[] objects = new Object[1]; + SimpleReflectionTestFixture simple = new SimpleReflectionTestFixture(objects); + objects[0] = simple; + assertEquals( + this.toBaseString(objects) + + "[{" + + this.toBaseString(simple) + + "[o=" + + this.toBaseString(objects) + + "]" + + "}]", + ToStringBuilder.reflectionToString(objects)); + assertEquals( + this.toBaseString(simple) + + "[o={" + + this.toBaseString(simple) + + "}]", + ToStringBuilder.reflectionToString(simple)); + } + + void validateNullToStringStyleRegistry() { + final Map registry = ToStringStyle.getRegistry(); + assertNull("Expected null, actual: "+registry, registry); + } + // End: Reflection cycle tests + + @Test + public void testAppendSuper() { + assertEquals(baseStr + "[]", new ToStringBuilder(base).appendSuper("Integer@8888[]").toString()); + assertEquals(baseStr + "[]", new ToStringBuilder(base).appendSuper("Integer@8888[]").toString()); + + assertEquals(baseStr + "[a=hello]", new ToStringBuilder(base).appendSuper("Integer@8888[]").append("a", "hello").toString()); + assertEquals(baseStr + "[,a=hello]", new ToStringBuilder(base).appendSuper("Integer@8888[]").append("a", "hello").toString()); + assertEquals(baseStr + "[a=hello]", new ToStringBuilder(base).appendSuper(null).append("a", "hello").toString()); + } + + @Test + public void testAppendToString() { + assertEquals(baseStr + "[]", new ToStringBuilder(base).appendToString("Integer@8888[]").toString()); + assertEquals(baseStr + "[]", new ToStringBuilder(base).appendToString("Integer@8888[]").toString()); + + assertEquals(baseStr + "[a=hello]", new ToStringBuilder(base).appendToString("Integer@8888[]").append("a", "hello").toString()); + assertEquals(baseStr + "[,a=hello]", new ToStringBuilder(base).appendToString("Integer@8888[]").append("a", "hello").toString()); + assertEquals(baseStr + "[a=hello]", new ToStringBuilder(base).appendToString(null).append("a", "hello").toString()); + } + + @Test + public void testObject() { + Integer i3 = Integer.valueOf(3); + Integer i4 = Integer.valueOf(4); + assertEquals(baseStr + "[]", new ToStringBuilder(base).append((Object) null).toString()); + assertEquals(baseStr + "[3]", new ToStringBuilder(base).append(i3).toString()); + assertEquals(baseStr + "[a=]", new ToStringBuilder(base).append("a", (Object) null).toString()); + assertEquals(baseStr + "[a=3]", new ToStringBuilder(base).append("a", i3).toString()); + assertEquals(baseStr + "[a=3,b=4]", new ToStringBuilder(base).append("a", i3).append("b", i4).toString()); + assertEquals(baseStr + "[a=]", new ToStringBuilder(base).append("a", i3, false).toString()); + assertEquals(baseStr + "[a=]", new ToStringBuilder(base).append("a", new ArrayList(), false).toString()); + assertEquals(baseStr + "[a=[]]", new ToStringBuilder(base).append("a", new ArrayList(), true).toString()); + assertEquals(baseStr + "[a=]", new ToStringBuilder(base).append("a", new HashMap(), false).toString()); + assertEquals(baseStr + "[a={}]", new ToStringBuilder(base).append("a", new HashMap(), true).toString()); + assertEquals(baseStr + "[a=]", new ToStringBuilder(base).append("a", (Object) new String[0], false).toString()); + assertEquals(baseStr + "[a={}]", new ToStringBuilder(base).append("a", (Object) new String[0], true).toString()); + } + + @Test + public void testObjectBuild() { + Integer i3 = Integer.valueOf(3); + Integer i4 = Integer.valueOf(4); + assertEquals(baseStr + "[]", new ToStringBuilder(base).append((Object) null).build()); + assertEquals(baseStr + "[3]", new ToStringBuilder(base).append(i3).build()); + assertEquals(baseStr + "[a=]", new ToStringBuilder(base).append("a", (Object) null).build()); + assertEquals(baseStr + "[a=3]", new ToStringBuilder(base).append("a", i3).build()); + assertEquals(baseStr + "[a=3,b=4]", new ToStringBuilder(base).append("a", i3).append("b", i4).build()); + assertEquals(baseStr + "[a=]", new ToStringBuilder(base).append("a", i3, false).build()); + assertEquals(baseStr + "[a=]", new ToStringBuilder(base).append("a", new ArrayList(), false).build()); + assertEquals(baseStr + "[a=[]]", new ToStringBuilder(base).append("a", new ArrayList(), true).build()); + assertEquals(baseStr + "[a=]", new ToStringBuilder(base).append("a", new HashMap(), false).build()); + assertEquals(baseStr + "[a={}]", new ToStringBuilder(base).append("a", new HashMap(), true).build()); + assertEquals(baseStr + "[a=]", new ToStringBuilder(base).append("a", (Object) new String[0], false).build()); + assertEquals(baseStr + "[a={}]", new ToStringBuilder(base).append("a", (Object) new String[0], true).build()); + } + + @Test + public void testLong() { + assertEquals(baseStr + "[3]", new ToStringBuilder(base).append(3L).toString()); + assertEquals(baseStr + "[a=3]", new ToStringBuilder(base).append("a", 3L).toString()); + assertEquals(baseStr + "[a=3,b=4]", new ToStringBuilder(base).append("a", 3L).append("b", 4L).toString()); + } + + @SuppressWarnings("cast") // cast is not really needed, keep for consistency + @Test + public void testInt() { + assertEquals(baseStr + "[3]", new ToStringBuilder(base).append((int) 3).toString()); + assertEquals(baseStr + "[a=3]", new ToStringBuilder(base).append("a", (int) 3).toString()); + assertEquals(baseStr + "[a=3,b=4]", new ToStringBuilder(base).append("a", (int) 3).append("b", (int) 4).toString()); + } + + @Test + public void testShort() { + assertEquals(baseStr + "[3]", new ToStringBuilder(base).append((short) 3).toString()); + assertEquals(baseStr + "[a=3]", new ToStringBuilder(base).append("a", (short) 3).toString()); + assertEquals(baseStr + "[a=3,b=4]", new ToStringBuilder(base).append("a", (short) 3).append("b", (short) 4).toString()); + } + + @Test + public void testChar() { + assertEquals(baseStr + "[A]", new ToStringBuilder(base).append((char) 65).toString()); + assertEquals(baseStr + "[a=A]", new ToStringBuilder(base).append("a", (char) 65).toString()); + assertEquals(baseStr + "[a=A,b=B]", new ToStringBuilder(base).append("a", (char) 65).append("b", (char) 66).toString()); + } + + @Test + public void testByte() { + assertEquals(baseStr + "[3]", new ToStringBuilder(base).append((byte) 3).toString()); + assertEquals(baseStr + "[a=3]", new ToStringBuilder(base).append("a", (byte) 3).toString()); + assertEquals(baseStr + "[a=3,b=4]", new ToStringBuilder(base).append("a", (byte) 3).append("b", (byte) 4).toString()); + } + + @SuppressWarnings("cast") + @Test + public void testDouble() { + assertEquals(baseStr + "[3.2]", new ToStringBuilder(base).append((double) 3.2).toString()); + assertEquals(baseStr + "[a=3.2]", new ToStringBuilder(base).append("a", (double) 3.2).toString()); + assertEquals(baseStr + "[a=3.2,b=4.3]", new ToStringBuilder(base).append("a", (double) 3.2).append("b", (double) 4.3).toString()); + } + + @Test + public void testFloat() { + assertEquals(baseStr + "[3.2]", new ToStringBuilder(base).append((float) 3.2).toString()); + assertEquals(baseStr + "[a=3.2]", new ToStringBuilder(base).append("a", (float) 3.2).toString()); + assertEquals(baseStr + "[a=3.2,b=4.3]", new ToStringBuilder(base).append("a", (float) 3.2).append("b", (float) 4.3).toString()); + } + + @Test + public void testBoolean() { + assertEquals(baseStr + "[true]", new ToStringBuilder(base).append(true).toString()); + assertEquals(baseStr + "[a=true]", new ToStringBuilder(base).append("a", true).toString()); + assertEquals(baseStr + "[a=true,b=false]", new ToStringBuilder(base).append("a", true).append("b", false).toString()); + } + + + @Test + public void testObjectArray() { + Object[] array = new Object[] {null, base, new int[] {3, 6}}; + assertEquals(baseStr + "[{,5,{3,6}}]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[{,5,{3,6}}]", new ToStringBuilder(base).append((Object) array).toString()); + array = null; + assertEquals(baseStr + "[]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[]", new ToStringBuilder(base).append((Object) array).toString()); + } + + @Test + public void testLongArray() { + long[] array = new long[] {1, 2, -3, 4}; + assertEquals(baseStr + "[{1,2,-3,4}]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[{1,2,-3,4}]", new ToStringBuilder(base).append((Object) array).toString()); + array = null; + assertEquals(baseStr + "[]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[]", new ToStringBuilder(base).append((Object) array).toString()); + } + + @Test + public void testIntArray() { + int[] array = new int[] {1, 2, -3, 4}; + assertEquals(baseStr + "[{1,2,-3,4}]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[{1,2,-3,4}]", new ToStringBuilder(base).append((Object) array).toString()); + array = null; + assertEquals(baseStr + "[]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[]", new ToStringBuilder(base).append((Object) array).toString()); + } + + @Test + public void testShortArray() { + short[] array = new short[] {1, 2, -3, 4}; + assertEquals(baseStr + "[{1,2,-3,4}]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[{1,2,-3,4}]", new ToStringBuilder(base).append((Object) array).toString()); + array = null; + assertEquals(baseStr + "[]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[]", new ToStringBuilder(base).append((Object) array).toString()); + } + + @Test + public void testByteArray() { + byte[] array = new byte[] {1, 2, -3, 4}; + assertEquals(baseStr + "[{1,2,-3,4}]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[{1,2,-3,4}]", new ToStringBuilder(base).append((Object) array).toString()); + array = null; + assertEquals(baseStr + "[]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[]", new ToStringBuilder(base).append((Object) array).toString()); + } + + @Test + public void testCharArray() { + char[] array = new char[] {'A', '2', '_', 'D'}; + assertEquals(baseStr + "[{A,2,_,D}]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[{A,2,_,D}]", new ToStringBuilder(base).append((Object) array).toString()); + array = null; + assertEquals(baseStr + "[]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[]", new ToStringBuilder(base).append((Object) array).toString()); + } + + @Test + public void testDoubleArray() { + double[] array = new double[] {1.0, 2.9876, -3.00001, 4.3}; + assertEquals(baseStr + "[{1.0,2.9876,-3.00001,4.3}]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[{1.0,2.9876,-3.00001,4.3}]", new ToStringBuilder(base).append((Object) array).toString()); + array = null; + assertEquals(baseStr + "[]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[]", new ToStringBuilder(base).append((Object) array).toString()); + } + + @Test + public void testFloatArray() { + float[] array = new float[] {1.0f, 2.9876f, -3.00001f, 4.3f}; + assertEquals(baseStr + "[{1.0,2.9876,-3.00001,4.3}]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[{1.0,2.9876,-3.00001,4.3}]", new ToStringBuilder(base).append((Object) array).toString()); + array = null; + assertEquals(baseStr + "[]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[]", new ToStringBuilder(base).append((Object) array).toString()); + } + + @Test + public void testBooleanArray() { + boolean[] array = new boolean[] {true, false, false}; + assertEquals(baseStr + "[{true,false,false}]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[{true,false,false}]", new ToStringBuilder(base).append((Object) array).toString()); + array = null; + assertEquals(baseStr + "[]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[]", new ToStringBuilder(base).append((Object) array).toString()); + } + + @Test + public void testLongArrayArray() { + long[][] array = new long[][] {{1, 2}, null, {5}}; + assertEquals(baseStr + "[{{1,2},,{5}}]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[{{1,2},,{5}}]", new ToStringBuilder(base).append((Object) array).toString()); + array = null; + assertEquals(baseStr + "[]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[]", new ToStringBuilder(base).append((Object) array).toString()); + } + + @Test + public void testIntArrayArray() { + int[][] array = new int[][] {{1, 2}, null, {5}}; + assertEquals(baseStr + "[{{1,2},,{5}}]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[{{1,2},,{5}}]", new ToStringBuilder(base).append((Object) array).toString()); + array = null; + assertEquals(baseStr + "[]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[]", new ToStringBuilder(base).append((Object) array).toString()); + } + + @Test + public void testShortArrayArray() { + short[][] array = new short[][] {{1, 2}, null, {5}}; + assertEquals(baseStr + "[{{1,2},,{5}}]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[{{1,2},,{5}}]", new ToStringBuilder(base).append((Object) array).toString()); + array = null; + assertEquals(baseStr + "[]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[]", new ToStringBuilder(base).append((Object) array).toString()); + } + + @Test + public void testByteArrayArray() { + byte[][] array = new byte[][] {{1, 2}, null, {5}}; + assertEquals(baseStr + "[{{1,2},,{5}}]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[{{1,2},,{5}}]", new ToStringBuilder(base).append((Object) array).toString()); + array = null; + assertEquals(baseStr + "[]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[]", new ToStringBuilder(base).append((Object) array).toString()); + } + + @Test + public void testCharArrayArray() { + char[][] array = new char[][] {{'A', 'B'}, null, {'p'}}; + assertEquals(baseStr + "[{{A,B},,{p}}]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[{{A,B},,{p}}]", new ToStringBuilder(base).append((Object) array).toString()); + array = null; + assertEquals(baseStr + "[]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[]", new ToStringBuilder(base).append((Object) array).toString()); + } + + @Test + public void testDoubleArrayArray() { + double[][] array = new double[][] {{1.0, 2.29686}, null, {Double.NaN}}; + assertEquals(baseStr + "[{{1.0,2.29686},,{NaN}}]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[{{1.0,2.29686},,{NaN}}]", new ToStringBuilder(base).append((Object) array).toString()); + array = null; + assertEquals(baseStr + "[]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[]", new ToStringBuilder(base).append((Object) array).toString()); + } + + @Test + public void testFloatArrayArray() { + float[][] array = new float[][] {{1.0f, 2.29686f}, null, {Float.NaN}}; + assertEquals(baseStr + "[{{1.0,2.29686},,{NaN}}]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[{{1.0,2.29686},,{NaN}}]", new ToStringBuilder(base).append((Object) array).toString()); + array = null; + assertEquals(baseStr + "[]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[]", new ToStringBuilder(base).append((Object) array).toString()); + } + + @Test + public void testBooleanArrayArray() { + boolean[][] array = new boolean[][] {{true, false}, null, {false}}; + assertEquals(baseStr + "[{{true,false},,{false}}]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[{{true,false},,{false}}]", new ToStringBuilder(base).append((Object) array).toString()); + array = null; + assertEquals(baseStr + "[]", new ToStringBuilder(base).append(array).toString()); + assertEquals(baseStr + "[]", new ToStringBuilder(base).append((Object) array).toString()); + } + + @Test + public void testObjectCycle() { + ObjectCycle a = new ObjectCycle(); + ObjectCycle b = new ObjectCycle(); + a.obj = b; + b.obj = a; + + String expected = toBaseString(a) + "[" + toBaseString(b) + "[" + toBaseString(a) + "]]"; + assertEquals(expected, a.toString()); + } + + static class ObjectCycle { + Object obj; + + @Override + public String toString() { + return new ToStringBuilder(this).append(obj).toString(); + } + } + + @Test + public void testSimpleReflectionStatics() { + SimpleReflectionStaticFieldsFixture instance1 = new SimpleReflectionStaticFieldsFixture(); + assertEquals( + this.toBaseString(instance1) + "[staticString=staticString,staticInt=12345]", + ReflectionToStringBuilder.toString(instance1, null, false, true, SimpleReflectionStaticFieldsFixture.class)); + assertEquals( + this.toBaseString(instance1) + "[staticString=staticString,staticInt=12345]", + ReflectionToStringBuilder.toString(instance1, null, true, true, SimpleReflectionStaticFieldsFixture.class)); + assertEquals( + this.toBaseString(instance1) + "[staticString=staticString,staticInt=12345]", + this.toStringWithStatics(instance1, null, SimpleReflectionStaticFieldsFixture.class)); + assertEquals( + this.toBaseString(instance1) + "[staticString=staticString,staticInt=12345]", + this.toStringWithStatics(instance1, null, SimpleReflectionStaticFieldsFixture.class)); + } + + /** + * Tests ReflectionToStringBuilder.toString() for statics. + */ + @Test + public void testReflectionStatics() { + ReflectionStaticFieldsFixture instance1 = new ReflectionStaticFieldsFixture(); + assertEquals( + this.toBaseString(instance1) + "[staticString=staticString,staticInt=12345,instanceString=instanceString,instanceInt=67890]", + ReflectionToStringBuilder.toString(instance1, null, false, true, ReflectionStaticFieldsFixture.class)); + assertEquals( + this.toBaseString(instance1) + "[staticString=staticString,staticInt=12345,staticTransientString=staticTransientString,staticTransientInt=54321,instanceString=instanceString,instanceInt=67890,transientString=transientString,transientInt=98765]", + ReflectionToStringBuilder.toString(instance1, null, true, true, ReflectionStaticFieldsFixture.class)); + assertEquals( + this.toBaseString(instance1) + "[staticString=staticString,staticInt=12345,instanceString=instanceString,instanceInt=67890]", + this.toStringWithStatics(instance1, null, ReflectionStaticFieldsFixture.class)); + assertEquals( + this.toBaseString(instance1) + "[staticString=staticString,staticInt=12345,instanceString=instanceString,instanceInt=67890]", + this.toStringWithStatics(instance1, null, ReflectionStaticFieldsFixture.class)); + } + + /** + * Tests ReflectionToStringBuilder.toString() for statics. + */ + @Test + public void testInheritedReflectionStatics() { + InheritedReflectionStaticFieldsFixture instance1 = new InheritedReflectionStaticFieldsFixture(); + assertEquals( + this.toBaseString(instance1) + "[staticString2=staticString2,staticInt2=67890]", + ReflectionToStringBuilder.toString(instance1, null, false, true, InheritedReflectionStaticFieldsFixture.class)); + assertEquals( + this.toBaseString(instance1) + "[staticString2=staticString2,staticInt2=67890,staticString=staticString,staticInt=12345]", + ReflectionToStringBuilder.toString(instance1, null, false, true, SimpleReflectionStaticFieldsFixture.class)); + assertEquals( + this.toBaseString(instance1) + "[staticString2=staticString2,staticInt2=67890,staticString=staticString,staticInt=12345]", + this.toStringWithStatics(instance1, null, SimpleReflectionStaticFieldsFixture.class)); + assertEquals( + this.toBaseString(instance1) + "[staticString2=staticString2,staticInt2=67890,staticString=staticString,staticInt=12345]", + this.toStringWithStatics(instance1, null, SimpleReflectionStaticFieldsFixture.class)); + } + + /** + *

This method uses reflection to build a suitable + * toString value which includes static fields.

+ * + *

It uses AccessibleObject.setAccessible to gain access to private + * fields. This means that it will throw a security exception if run + * under a security manager, if the permissions are not set up correctly. + * It is also not as efficient as testing explicitly.

+ * + *

Transient fields are not output.

+ * + *

Superclass fields will be appended up to and including the specified superclass. + * A null superclass is treated as java.lang.Object.

+ * + *

If the style is null, the default + * ToStringStyle is used.

+ * + * @param object the Object to be output + * @param style the style of the toString to create, + * may be null + * @param reflectUpToClass the superclass to reflect up to (inclusive), + * may be null + * @return the String result + * @throws IllegalArgumentException if the Object is null + */ + public String toStringWithStatics(T object, ToStringStyle style, Class reflectUpToClass) { + return ReflectionToStringBuilder.toString(object, style, false, true, reflectUpToClass); + } + + /** + * Tests ReflectionToStringBuilder setUpToClass(). + */ + @Test + public void test_setUpToClass_valid() { + Integer val = Integer.valueOf(5); + ReflectionToStringBuilder test = new ReflectionToStringBuilder(val); + test.setUpToClass(Number.class); + test.toString(); + } + + /** + * Tests ReflectionToStringBuilder setUpToClass(). + */ + @Test + public void test_setUpToClass_invalid() { + Integer val = Integer.valueOf(5); + ReflectionToStringBuilder test = new ReflectionToStringBuilder(val); + try { + test.setUpToClass(String.class); + fail(); + } catch (IllegalArgumentException ex) { + // expected + } finally { + test.toString(); + } + } + + /** + * Tests ReflectionToStringBuilder.toString() for statics. + */ + class ReflectionStaticFieldsFixture { + static final String staticString = "staticString"; + static final int staticInt = 12345; + static final transient String staticTransientString = "staticTransientString"; + static final transient int staticTransientInt = 54321; + String instanceString = "instanceString"; + int instanceInt = 67890; + transient String transientString = "transientString"; + transient int transientInt = 98765; + } + + /** + * Test fixture for ReflectionToStringBuilder.toString() for statics. + */ + class SimpleReflectionStaticFieldsFixture { + static final String staticString = "staticString"; + static final int staticInt = 12345; + } + + /** + * Test fixture for ReflectionToStringBuilder.toString() for statics. + */ + class InheritedReflectionStaticFieldsFixture extends SimpleReflectionStaticFieldsFixture { + static final String staticString2 = "staticString2"; + static final int staticInt2 = 67890; + } + + @Test + public void testReflectionNull() { + assertEquals("", ReflectionToStringBuilder.toString(null)); + } + + /** + * Points out failure to print anything from appendToString methods using MULTI_LINE_STYLE. + * See issue LANG-372. + */ + class MultiLineTestObject { + Integer i = Integer.valueOf(31337); + @Override + public String toString() { + return new ToStringBuilder(this).append("testInt", i).toString(); + } + } + + @Test + public void testAppendToStringUsingMultiLineStyle() { + MultiLineTestObject obj = new MultiLineTestObject(); + ToStringBuilder testBuilder = new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE) + .appendToString(obj.toString()); + assertEquals(testBuilder.toString().indexOf("testInt=31337"), -1); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/builder/ToStringStyle.java b/ApacheCommonsLang/org/apache/commons/lang3/builder/ToStringStyle.java new file mode 100644 index 0000000..2697245 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/builder/ToStringStyle.java @@ -0,0 +1,2280 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.builder; + +import java.io.Serializable; +import java.lang.reflect.Array; +import java.util.Collection; +import java.util.Map; +import java.util.WeakHashMap; + +import org.apache.commons.lang3.ClassUtils; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.SystemUtils; + +/** + *

Controls String formatting for {@link ToStringBuilder}. + * The main public interface is always via ToStringBuilder.

+ * + *

These classes are intended to be used as Singletons. + * There is no need to instantiate a new style each time. A program + * will generally use one of the predefined constants on this class. + * Alternatively, the {@link StandardToStringStyle} class can be used + * to set the individual settings. Thus most styles can be achieved + * without subclassing.

+ * + *

If required, a subclass can override as many or as few of the + * methods as it requires. Each object type (from boolean + * to long to Object to int[]) has + * its own methods to output it. Most have two versions, detail and summary. + * + *

For example, the detail version of the array based methods will + * output the whole array, whereas the summary method will just output + * the array length.

+ * + *

If you want to format the output of certain objects, such as dates, you + * must create a subclass and override a method. + *

+ * public class MyStyle extends ToStringStyle {
+ *   protected void appendDetail(StringBuffer buffer, String fieldName, Object value) {
+ *     if (value instanceof Date) {
+ *       value = new SimpleDateFormat("yyyy-MM-dd").format(value);
+ *     }
+ *     buffer.append(value);
+ *   }
+ * }
+ * 
+ *

+ * + * @since 1.0 + * @version $Id: ToStringStyle.java 1298506 2012-03-08 18:50:52Z sebb $ + */ +public abstract class ToStringStyle implements Serializable { + + /** + * Serialization version ID. + */ + private static final long serialVersionUID = -2587890625525655916L; + + /** + * The default toString style. Using the Using the Person + * example from {@link ToStringBuilder}, the output would look like this: + * + *
+     * Person@182f0db[name=John Doe,age=33,smoker=false]
+     * 
+ */ + public static final ToStringStyle DEFAULT_STYLE = new DefaultToStringStyle(); + + /** + * The multi line toString style. Using the Using the Person + * example from {@link ToStringBuilder}, the output would look like this: + * + *
+     * Person@182f0db[
+     *   name=John Doe
+     *   age=33
+     *   smoker=false
+     * ]
+     * 
+ */ + public static final ToStringStyle MULTI_LINE_STYLE = new MultiLineToStringStyle(); + + /** + * The no field names toString style. Using the Using the + * Person example from {@link ToStringBuilder}, the output + * would look like this: + * + *
+     * Person@182f0db[John Doe,33,false]
+     * 
+ */ + public static final ToStringStyle NO_FIELD_NAMES_STYLE = new NoFieldNameToStringStyle(); + + /** + * The short prefix toString style. Using the Person example + * from {@link ToStringBuilder}, the output would look like this: + * + *
+     * Person[name=John Doe,age=33,smoker=false]
+     * 
+ * + * @since 2.1 + */ + public static final ToStringStyle SHORT_PREFIX_STYLE = new ShortPrefixToStringStyle(); + + /** + * The simple toString style. Using the Using the Person + * example from {@link ToStringBuilder}, the output would look like this: + * + *
+     * John Doe,33,false
+     * 
+ */ + public static final ToStringStyle SIMPLE_STYLE = new SimpleToStringStyle(); + + /** + *

+ * A registry of objects used by reflectionToString methods + * to detect cyclical object references and avoid infinite loops. + *

+ */ + private static final ThreadLocal> REGISTRY = + new ThreadLocal>(); + /* + * Note that objects of this class are generally shared between threads, so + * an instance variable would not be suitable here. + * + * In normal use the registry should always be left empty, because the caller + * should call toString() which will clean up. + * + * See LANG-792 + */ + + /** + *

+ * Returns the registry of objects being traversed by the reflectionToString + * methods in the current thread. + *

+ * + * @return Set the registry of objects being traversed + */ + static Map getRegistry() { + return REGISTRY.get(); + } + + /** + *

+ * Returns true if the registry contains the given object. + * Used by the reflection methods to avoid infinite loops. + *

+ * + * @param value + * The object to lookup in the registry. + * @return boolean true if the registry contains the given + * object. + */ + static boolean isRegistered(Object value) { + Map m = getRegistry(); + return m != null && m.containsKey(value); + } + + /** + *

+ * Registers the given object. Used by the reflection methods to avoid + * infinite loops. + *

+ * + * @param value + * The object to register. + */ + static void register(Object value) { + if (value != null) { + Map m = getRegistry(); + if (m == null) { + REGISTRY.set(new WeakHashMap()); + } + getRegistry().put(value, null); + } + } + + /** + *

+ * Unregisters the given object. + *

+ * + *

+ * Used by the reflection methods to avoid infinite loops. + *

+ * + * @param value + * The object to unregister. + */ + static void unregister(Object value) { + if (value != null) { + Map m = getRegistry(); + if (m != null) { + m.remove(value); + if (m.isEmpty()) { + REGISTRY.remove(); + } + } + } + } + + /** + * Whether to use the field names, the default is true. + */ + private boolean useFieldNames = true; + + /** + * Whether to use the class name, the default is true. + */ + private boolean useClassName = true; + + /** + * Whether to use short class names, the default is false. + */ + private boolean useShortClassName = false; + + /** + * Whether to use the identity hash code, the default is true. + */ + private boolean useIdentityHashCode = true; + + /** + * The content start '['. + */ + private String contentStart = "["; + + /** + * The content end ']'. + */ + private String contentEnd = "]"; + + /** + * The field name value separator '='. + */ + private String fieldNameValueSeparator = "="; + + /** + * Whether the field separator should be added before any other fields. + */ + private boolean fieldSeparatorAtStart = false; + + /** + * Whether the field separator should be added after any other fields. + */ + private boolean fieldSeparatorAtEnd = false; + + /** + * The field separator ','. + */ + private String fieldSeparator = ","; + + /** + * The array start '{'. + */ + private String arrayStart = "{"; + + /** + * The array separator ','. + */ + private String arraySeparator = ","; + + /** + * The detail for array content. + */ + private boolean arrayContentDetail = true; + + /** + * The array end '}'. + */ + private String arrayEnd = "}"; + + /** + * The value to use when fullDetail is null, + * the default value is true. + */ + private boolean defaultFullDetail = true; + + /** + * The null text '<null>'. + */ + private String nullText = ""; + + /** + * The summary size text start '. + */ + private String sizeStartText = "'>'. + */ + private String sizeEndText = ">"; + + /** + * The summary object text start '<'. + */ + private String summaryObjectStartText = "<"; + + /** + * The summary object text start '>'. + */ + private String summaryObjectEndText = ">"; + + //---------------------------------------------------------------------------- + + /** + *

Constructor.

+ */ + protected ToStringStyle() { + super(); + } + + //---------------------------------------------------------------------------- + + /** + *

Append to the toString the superclass toString.

+ *

NOTE: It assumes that the toString has been created from the same ToStringStyle.

+ * + *

A null superToString is ignored.

+ * + * @param buffer the StringBuffer to populate + * @param superToString the super.toString() + * @since 2.0 + */ + public void appendSuper(StringBuffer buffer, String superToString) { + appendToString(buffer, superToString); + } + + /** + *

Append to the toString another toString.

+ *

NOTE: It assumes that the toString has been created from the same ToStringStyle.

+ * + *

A null toString is ignored.

+ * + * @param buffer the StringBuffer to populate + * @param toString the additional toString + * @since 2.0 + */ + public void appendToString(StringBuffer buffer, String toString) { + if (toString != null) { + int pos1 = toString.indexOf(contentStart) + contentStart.length(); + int pos2 = toString.lastIndexOf(contentEnd); + if (pos1 != pos2 && pos1 >= 0 && pos2 >= 0) { + String data = toString.substring(pos1, pos2); + if (fieldSeparatorAtStart) { + removeLastFieldSeparator(buffer); + } + buffer.append(data); + appendFieldSeparator(buffer); + } + } + } + + /** + *

Append to the toString the start of data indicator.

+ * + * @param buffer the StringBuffer to populate + * @param object the Object to build a toString for + */ + public void appendStart(StringBuffer buffer, Object object) { + if (object != null) { + appendClassName(buffer, object); + appendIdentityHashCode(buffer, object); + appendContentStart(buffer); + if (fieldSeparatorAtStart) { + appendFieldSeparator(buffer); + } + } + } + + /** + *

Append to the toString the end of data indicator.

+ * + * @param buffer the StringBuffer to populate + * @param object the Object to build a + * toString for. + */ + public void appendEnd(StringBuffer buffer, Object object) { + if (this.fieldSeparatorAtEnd == false) { + removeLastFieldSeparator(buffer); + } + appendContentEnd(buffer); + unregister(object); + } + + /** + *

Remove the last field separator from the buffer.

+ * + * @param buffer the StringBuffer to populate + * @since 2.0 + */ + protected void removeLastFieldSeparator(StringBuffer buffer) { + int len = buffer.length(); + int sepLen = fieldSeparator.length(); + if (len > 0 && sepLen > 0 && len >= sepLen) { + boolean match = true; + for (int i = 0; i < sepLen; i++) { + if (buffer.charAt(len - 1 - i) != fieldSeparator.charAt(sepLen - 1 - i)) { + match = false; + break; + } + } + if (match) { + buffer.setLength(len - sepLen); + } + } + } + + //---------------------------------------------------------------------------- + + /** + *

Append to the toString an Object + * value, printing the full toString of the + * Object passed in.

+ * + * @param buffer the StringBuffer to populate + * @param fieldName the field name + * @param value the value to add to the toString + * @param fullDetail true for detail, false + * for summary info, null for style decides + */ + public void append(StringBuffer buffer, String fieldName, Object value, Boolean fullDetail) { + appendFieldStart(buffer, fieldName); + + if (value == null) { + appendNullText(buffer, fieldName); + + } else { + appendInternal(buffer, fieldName, value, isFullDetail(fullDetail)); + } + + appendFieldEnd(buffer, fieldName); + } + + /** + *

Append to the toString an Object, + * correctly interpreting its type.

+ * + *

This method performs the main lookup by Class type to correctly + * route arrays, Collections, Maps and + * Objects to the appropriate method.

+ * + *

Either detail or summary views can be specified.

+ * + *

If a cycle is detected, an object will be appended with the + * Object.toString() format.

+ * + * @param buffer the StringBuffer to populate + * @param fieldName the field name, typically not used as already appended + * @param value the value to add to the toString, + * not null + * @param detail output detail or not + */ + protected void appendInternal(StringBuffer buffer, String fieldName, Object value, boolean detail) { + if (isRegistered(value) + && !(value instanceof Number || value instanceof Boolean || value instanceof Character)) { + appendCyclicObject(buffer, fieldName, value); + return; + } + + register(value); + + try { + if (value instanceof Collection) { + if (detail) { + appendDetail(buffer, fieldName, (Collection) value); + } else { + appendSummarySize(buffer, fieldName, ((Collection) value).size()); + } + + } else if (value instanceof Map) { + if (detail) { + appendDetail(buffer, fieldName, (Map) value); + } else { + appendSummarySize(buffer, fieldName, ((Map) value).size()); + } + + } else if (value instanceof long[]) { + if (detail) { + appendDetail(buffer, fieldName, (long[]) value); + } else { + appendSummary(buffer, fieldName, (long[]) value); + } + + } else if (value instanceof int[]) { + if (detail) { + appendDetail(buffer, fieldName, (int[]) value); + } else { + appendSummary(buffer, fieldName, (int[]) value); + } + + } else if (value instanceof short[]) { + if (detail) { + appendDetail(buffer, fieldName, (short[]) value); + } else { + appendSummary(buffer, fieldName, (short[]) value); + } + + } else if (value instanceof byte[]) { + if (detail) { + appendDetail(buffer, fieldName, (byte[]) value); + } else { + appendSummary(buffer, fieldName, (byte[]) value); + } + + } else if (value instanceof char[]) { + if (detail) { + appendDetail(buffer, fieldName, (char[]) value); + } else { + appendSummary(buffer, fieldName, (char[]) value); + } + + } else if (value instanceof double[]) { + if (detail) { + appendDetail(buffer, fieldName, (double[]) value); + } else { + appendSummary(buffer, fieldName, (double[]) value); + } + + } else if (value instanceof float[]) { + if (detail) { + appendDetail(buffer, fieldName, (float[]) value); + } else { + appendSummary(buffer, fieldName, (float[]) value); + } + + } else if (value instanceof boolean[]) { + if (detail) { + appendDetail(buffer, fieldName, (boolean[]) value); + } else { + appendSummary(buffer, fieldName, (boolean[]) value); + } + + } else if (value.getClass().isArray()) { + if (detail) { + appendDetail(buffer, fieldName, (Object[]) value); + } else { + appendSummary(buffer, fieldName, (Object[]) value); + } + + } else { + if (detail) { + appendDetail(buffer, fieldName, value); + } else { + appendSummary(buffer, fieldName, value); + } + } + } finally { + unregister(value); + } + } + + /** + *

Append to the toString an Object + * value that has been detected to participate in a cycle. This + * implementation will print the standard string value of the value.

+ * + * @param buffer the StringBuffer to populate + * @param fieldName the field name, typically not used as already appended + * @param value the value to add to the toString, + * not null + * + * @since 2.2 + */ + protected void appendCyclicObject(StringBuffer buffer, String fieldName, Object value) { + ObjectUtils.identityToString(buffer, value); + } + + /** + *

Append to the toString an Object + * value, printing the full detail of the Object.

+ * + * @param buffer the StringBuffer to populate + * @param fieldName the field name, typically not used as already appended + * @param value the value to add to the toString, + * not null + */ + protected void appendDetail(StringBuffer buffer, String fieldName, Object value) { + buffer.append(value); + } + + /** + *

Append to the toString a Collection.

+ * + * @param buffer the StringBuffer to populate + * @param fieldName the field name, typically not used as already appended + * @param coll the Collection to add to the + * toString, not null + */ + protected void appendDetail(StringBuffer buffer, String fieldName, Collection coll) { + buffer.append(coll); + } + + /** + *

Append to the toString a Map.

+ * + * @param buffer the StringBuffer to populate + * @param fieldName the field name, typically not used as already appended + * @param map the Map to add to the toString, + * not null + */ + protected void appendDetail(StringBuffer buffer, String fieldName, Map map) { + buffer.append(map); + } + + /** + *

Append to the toString an Object + * value, printing a summary of the Object.

+ * + * @param buffer the StringBuffer to populate + * @param fieldName the field name, typically not used as already appended + * @param value the value to add to the toString, + * not null + */ + protected void appendSummary(StringBuffer buffer, String fieldName, Object value) { + buffer.append(summaryObjectStartText); + buffer.append(getShortClassName(value.getClass())); + buffer.append(summaryObjectEndText); + } + + //---------------------------------------------------------------------------- + + /** + *

Append to the toString a long + * value.

+ * + * @param buffer the StringBuffer to populate + * @param fieldName the field name + * @param value the value to add to the toString + */ + public void append(StringBuffer buffer, String fieldName, long value) { + appendFieldStart(buffer, fieldName); + appendDetail(buffer, fieldName, value); + appendFieldEnd(buffer, fieldName); + } + + /** + *

Append to the toString a long + * value.

+ * + * @param buffer the StringBuffer to populate + * @param fieldName the field name, typically not used as already appended + * @param value the value to add to the toString + */ + protected void appendDetail(StringBuffer buffer, String fieldName, long value) { + buffer.append(value); + } + + //---------------------------------------------------------------------------- + + /** + *

Append to the toString an int + * value.

+ * + * @param buffer the StringBuffer to populate + * @param fieldName the field name + * @param value the value to add to the toString + */ + public void append(StringBuffer buffer, String fieldName, int value) { + appendFieldStart(buffer, fieldName); + appendDetail(buffer, fieldName, value); + appendFieldEnd(buffer, fieldName); + } + + /** + *

Append to the toString an int + * value.

+ * + * @param buffer the StringBuffer to populate + * @param fieldName the field name, typically not used as already appended + * @param value the value to add to the toString + */ + protected void appendDetail(StringBuffer buffer, String fieldName, int value) { + buffer.append(value); + } + + //---------------------------------------------------------------------------- + + /** + *

Append to the toString a short + * value.

+ * + * @param buffer the StringBuffer to populate + * @param fieldName the field name + * @param value the value to add to the toString + */ + public void append(StringBuffer buffer, String fieldName, short value) { + appendFieldStart(buffer, fieldName); + appendDetail(buffer, fieldName, value); + appendFieldEnd(buffer, fieldName); + } + + /** + *

Append to the toString a short + * value.

+ * + * @param buffer the StringBuffer to populate + * @param fieldName the field name, typically not used as already appended + * @param value the value to add to the toString + */ + protected void appendDetail(StringBuffer buffer, String fieldName, short value) { + buffer.append(value); + } + + //---------------------------------------------------------------------------- + + /** + *

Append to the toString a byte + * value.

+ * + * @param buffer the StringBuffer to populate + * @param fieldName the field name + * @param value the value to add to the toString + */ + public void append(StringBuffer buffer, String fieldName, byte value) { + appendFieldStart(buffer, fieldName); + appendDetail(buffer, fieldName, value); + appendFieldEnd(buffer, fieldName); + } + + /** + *

Append to the toString a byte + * value.

+ * + * @param buffer the StringBuffer to populate + * @param fieldName the field name, typically not used as already appended + * @param value the value to add to the toString + */ + protected void appendDetail(StringBuffer buffer, String fieldName, byte value) { + buffer.append(value); + } + + //---------------------------------------------------------------------------- + + /** + *

Append to the toString a char + * value.

+ * + * @param buffer the StringBuffer to populate + * @param fieldName the field name + * @param value the value to add to the toString + */ + public void append(StringBuffer buffer, String fieldName, char value) { + appendFieldStart(buffer, fieldName); + appendDetail(buffer, fieldName, value); + appendFieldEnd(buffer, fieldName); + } + + /** + *

Append to the toString a char + * value.

+ * + * @param buffer the StringBuffer to populate + * @param fieldName the field name, typically not used as already appended + * @param value the value to add to the toString + */ + protected void appendDetail(StringBuffer buffer, String fieldName, char value) { + buffer.append(value); + } + + //---------------------------------------------------------------------------- + + /** + *

Append to the toString a double + * value.

+ * + * @param buffer the StringBuffer to populate + * @param fieldName the field name + * @param value the value to add to the toString + */ + public void append(StringBuffer buffer, String fieldName, double value) { + appendFieldStart(buffer, fieldName); + appendDetail(buffer, fieldName, value); + appendFieldEnd(buffer, fieldName); + } + + /** + *

Append to the toString a double + * value.

+ * + * @param buffer the StringBuffer to populate + * @param fieldName the field name, typically not used as already appended + * @param value the value to add to the toString + */ + protected void appendDetail(StringBuffer buffer, String fieldName, double value) { + buffer.append(value); + } + + //---------------------------------------------------------------------------- + + /** + *

Append to the toString a float + * value.

+ * + * @param buffer the StringBuffer to populate + * @param fieldName the field name + * @param value the value to add to the toString + */ + public void append(StringBuffer buffer, String fieldName, float value) { + appendFieldStart(buffer, fieldName); + appendDetail(buffer, fieldName, value); + appendFieldEnd(buffer, fieldName); + } + + /** + *

Append to the toString a float + * value.

+ * + * @param buffer the StringBuffer to populate + * @param fieldName the field name, typically not used as already appended + * @param value the value to add to the toString + */ + protected void appendDetail(StringBuffer buffer, String fieldName, float value) { + buffer.append(value); + } + + //---------------------------------------------------------------------------- + + /** + *

Append to the toString a boolean + * value.

+ * + * @param buffer the StringBuffer to populate + * @param fieldName the field name + * @param value the value to add to the toString + */ + public void append(StringBuffer buffer, String fieldName, boolean value) { + appendFieldStart(buffer, fieldName); + appendDetail(buffer, fieldName, value); + appendFieldEnd(buffer, fieldName); + } + + /** + *

Append to the toString a boolean + * value.

+ * + * @param buffer the StringBuffer to populate + * @param fieldName the field name, typically not used as already appended + * @param value the value to add to the toString + */ + protected void appendDetail(StringBuffer buffer, String fieldName, boolean value) { + buffer.append(value); + } + + /** + *

Append to the toString an Object + * array.

+ * + * @param buffer the StringBuffer to populate + * @param fieldName the field name + * @param array the array to add to the toString + * @param fullDetail true for detail, false + * for summary info, null for style decides + */ + public void append(StringBuffer buffer, String fieldName, Object[] array, Boolean fullDetail) { + appendFieldStart(buffer, fieldName); + + if (array == null) { + appendNullText(buffer, fieldName); + + } else if (isFullDetail(fullDetail)) { + appendDetail(buffer, fieldName, array); + + } else { + appendSummary(buffer, fieldName, array); + } + + appendFieldEnd(buffer, fieldName); + } + + //---------------------------------------------------------------------------- + + /** + *

Append to the toString the detail of an + * Object array.

+ * + * @param buffer the StringBuffer to populate + * @param fieldName the field name, typically not used as already appended + * @param array the array to add to the toString, + * not null + */ + protected void appendDetail(StringBuffer buffer, String fieldName, Object[] array) { + buffer.append(arrayStart); + for (int i = 0; i < array.length; i++) { + Object item = array[i]; + if (i > 0) { + buffer.append(arraySeparator); + } + if (item == null) { + appendNullText(buffer, fieldName); + + } else { + appendInternal(buffer, fieldName, item, arrayContentDetail); + } + } + buffer.append(arrayEnd); + } + + /** + *

Append to the toString the detail of an array type.

+ * + * @param buffer the StringBuffer to populate + * @param fieldName the field name, typically not used as already appended + * @param array the array to add to the toString, + * not null + * @since 2.0 + */ + protected void reflectionAppendArrayDetail(StringBuffer buffer, String fieldName, Object array) { + buffer.append(arrayStart); + int length = Array.getLength(array); + for (int i = 0; i < length; i++) { + Object item = Array.get(array, i); + if (i > 0) { + buffer.append(arraySeparator); + } + if (item == null) { + appendNullText(buffer, fieldName); + + } else { + appendInternal(buffer, fieldName, item, arrayContentDetail); + } + } + buffer.append(arrayEnd); + } + + /** + *

Append to the toString a summary of an + * Object array.

+ * + * @param buffer the StringBuffer to populate + * @param fieldName the field name, typically not used as already appended + * @param array the array to add to the toString, + * not null + */ + protected void appendSummary(StringBuffer buffer, String fieldName, Object[] array) { + appendSummarySize(buffer, fieldName, array.length); + } + + //---------------------------------------------------------------------------- + + /** + *

Append to the toString a long + * array.

+ * + * @param buffer the StringBuffer to populate + * @param fieldName the field name + * @param array the array to add to the toString + * @param fullDetail true for detail, false + * for summary info, null for style decides + */ + public void append(StringBuffer buffer, String fieldName, long[] array, Boolean fullDetail) { + appendFieldStart(buffer, fieldName); + + if (array == null) { + appendNullText(buffer, fieldName); + + } else if (isFullDetail(fullDetail)) { + appendDetail(buffer, fieldName, array); + + } else { + appendSummary(buffer, fieldName, array); + } + + appendFieldEnd(buffer, fieldName); + } + + /** + *

Append to the toString the detail of a + * long array.

+ * + * @param buffer the StringBuffer to populate + * @param fieldName the field name, typically not used as already appended + * @param array the array to add to the toString, + * not null + */ + protected void appendDetail(StringBuffer buffer, String fieldName, long[] array) { + buffer.append(arrayStart); + for (int i = 0; i < array.length; i++) { + if (i > 0) { + buffer.append(arraySeparator); + } + appendDetail(buffer, fieldName, array[i]); + } + buffer.append(arrayEnd); + } + + /** + *

Append to the toString a summary of a + * long array.

+ * + * @param buffer the StringBuffer to populate + * @param fieldName the field name, typically not used as already appended + * @param array the array to add to the toString, + * not null + */ + protected void appendSummary(StringBuffer buffer, String fieldName, long[] array) { + appendSummarySize(buffer, fieldName, array.length); + } + + //---------------------------------------------------------------------------- + + /** + *

Append to the toString an int + * array.

+ * + * @param buffer the StringBuffer to populate + * @param fieldName the field name + * @param array the array to add to the toString + * @param fullDetail true for detail, false + * for summary info, null for style decides + */ + public void append(StringBuffer buffer, String fieldName, int[] array, Boolean fullDetail) { + appendFieldStart(buffer, fieldName); + + if (array == null) { + appendNullText(buffer, fieldName); + + } else if (isFullDetail(fullDetail)) { + appendDetail(buffer, fieldName, array); + + } else { + appendSummary(buffer, fieldName, array); + } + + appendFieldEnd(buffer, fieldName); + } + + /** + *

Append to the toString the detail of an + * int array.

+ * + * @param buffer the StringBuffer to populate + * @param fieldName the field name, typically not used as already appended + * @param array the array to add to the toString, + * not null + */ + protected void appendDetail(StringBuffer buffer, String fieldName, int[] array) { + buffer.append(arrayStart); + for (int i = 0; i < array.length; i++) { + if (i > 0) { + buffer.append(arraySeparator); + } + appendDetail(buffer, fieldName, array[i]); + } + buffer.append(arrayEnd); + } + + /** + *

Append to the toString a summary of an + * int array.

+ * + * @param buffer the StringBuffer to populate + * @param fieldName the field name, typically not used as already appended + * @param array the array to add to the toString, + * not null + */ + protected void appendSummary(StringBuffer buffer, String fieldName, int[] array) { + appendSummarySize(buffer, fieldName, array.length); + } + + //---------------------------------------------------------------------------- + + /** + *

Append to the toString a short + * array.

+ * + * @param buffer the StringBuffer to populate + * @param fieldName the field name + * @param array the array to add to the toString + * @param fullDetail true for detail, false + * for summary info, null for style decides + */ + public void append(StringBuffer buffer, String fieldName, short[] array, Boolean fullDetail) { + appendFieldStart(buffer, fieldName); + + if (array == null) { + appendNullText(buffer, fieldName); + + } else if (isFullDetail(fullDetail)) { + appendDetail(buffer, fieldName, array); + + } else { + appendSummary(buffer, fieldName, array); + } + + appendFieldEnd(buffer, fieldName); + } + + /** + *

Append to the toString the detail of a + * short array.

+ * + * @param buffer the StringBuffer to populate + * @param fieldName the field name, typically not used as already appended + * @param array the array to add to the toString, + * not null + */ + protected void appendDetail(StringBuffer buffer, String fieldName, short[] array) { + buffer.append(arrayStart); + for (int i = 0; i < array.length; i++) { + if (i > 0) { + buffer.append(arraySeparator); + } + appendDetail(buffer, fieldName, array[i]); + } + buffer.append(arrayEnd); + } + + /** + *

Append to the toString a summary of a + * short array.

+ * + * @param buffer the StringBuffer to populate + * @param fieldName the field name, typically not used as already appended + * @param array the array to add to the toString, + * not null + */ + protected void appendSummary(StringBuffer buffer, String fieldName, short[] array) { + appendSummarySize(buffer, fieldName, array.length); + } + + //---------------------------------------------------------------------------- + + /** + *

Append to the toString a byte + * array.

+ * + * @param buffer the StringBuffer to populate + * @param fieldName the field name + * @param array the array to add to the toString + * @param fullDetail true for detail, false + * for summary info, null for style decides + */ + public void append(StringBuffer buffer, String fieldName, byte[] array, Boolean fullDetail) { + appendFieldStart(buffer, fieldName); + + if (array == null) { + appendNullText(buffer, fieldName); + + } else if (isFullDetail(fullDetail)) { + appendDetail(buffer, fieldName, array); + + } else { + appendSummary(buffer, fieldName, array); + } + + appendFieldEnd(buffer, fieldName); + } + + /** + *

Append to the toString the detail of a + * byte array.

+ * + * @param buffer the StringBuffer to populate + * @param fieldName the field name, typically not used as already appended + * @param array the array to add to the toString, + * not null + */ + protected void appendDetail(StringBuffer buffer, String fieldName, byte[] array) { + buffer.append(arrayStart); + for (int i = 0; i < array.length; i++) { + if (i > 0) { + buffer.append(arraySeparator); + } + appendDetail(buffer, fieldName, array[i]); + } + buffer.append(arrayEnd); + } + + /** + *

Append to the toString a summary of a + * byte array.

+ * + * @param buffer the StringBuffer to populate + * @param fieldName the field name, typically not used as already appended + * @param array the array to add to the toString, + * not null + */ + protected void appendSummary(StringBuffer buffer, String fieldName, byte[] array) { + appendSummarySize(buffer, fieldName, array.length); + } + + //---------------------------------------------------------------------------- + + /** + *

Append to the toString a char + * array.

+ * + * @param buffer the StringBuffer to populate + * @param fieldName the field name + * @param array the array to add to the toString + * @param fullDetail true for detail, false + * for summary info, null for style decides + */ + public void append(StringBuffer buffer, String fieldName, char[] array, Boolean fullDetail) { + appendFieldStart(buffer, fieldName); + + if (array == null) { + appendNullText(buffer, fieldName); + + } else if (isFullDetail(fullDetail)) { + appendDetail(buffer, fieldName, array); + + } else { + appendSummary(buffer, fieldName, array); + } + + appendFieldEnd(buffer, fieldName); + } + + /** + *

Append to the toString the detail of a + * char array.

+ * + * @param buffer the StringBuffer to populate + * @param fieldName the field name, typically not used as already appended + * @param array the array to add to the toString, + * not null + */ + protected void appendDetail(StringBuffer buffer, String fieldName, char[] array) { + buffer.append(arrayStart); + for (int i = 0; i < array.length; i++) { + if (i > 0) { + buffer.append(arraySeparator); + } + appendDetail(buffer, fieldName, array[i]); + } + buffer.append(arrayEnd); + } + + /** + *

Append to the toString a summary of a + * char array.

+ * + * @param buffer the StringBuffer to populate + * @param fieldName the field name, typically not used as already appended + * @param array the array to add to the toString, + * not null + */ + protected void appendSummary(StringBuffer buffer, String fieldName, char[] array) { + appendSummarySize(buffer, fieldName, array.length); + } + + //---------------------------------------------------------------------------- + + /** + *

Append to the toString a double + * array.

+ * + * @param buffer the StringBuffer to populate + * @param fieldName the field name + * @param array the array to add to the toString + * @param fullDetail true for detail, false + * for summary info, null for style decides + */ + public void append(StringBuffer buffer, String fieldName, double[] array, Boolean fullDetail) { + appendFieldStart(buffer, fieldName); + + if (array == null) { + appendNullText(buffer, fieldName); + + } else if (isFullDetail(fullDetail)) { + appendDetail(buffer, fieldName, array); + + } else { + appendSummary(buffer, fieldName, array); + } + + appendFieldEnd(buffer, fieldName); + } + + /** + *

Append to the toString the detail of a + * double array.

+ * + * @param buffer the StringBuffer to populate + * @param fieldName the field name, typically not used as already appended + * @param array the array to add to the toString, + * not null + */ + protected void appendDetail(StringBuffer buffer, String fieldName, double[] array) { + buffer.append(arrayStart); + for (int i = 0; i < array.length; i++) { + if (i > 0) { + buffer.append(arraySeparator); + } + appendDetail(buffer, fieldName, array[i]); + } + buffer.append(arrayEnd); + } + + /** + *

Append to the toString a summary of a + * double array.

+ * + * @param buffer the StringBuffer to populate + * @param fieldName the field name, typically not used as already appended + * @param array the array to add to the toString, + * not null + */ + protected void appendSummary(StringBuffer buffer, String fieldName, double[] array) { + appendSummarySize(buffer, fieldName, array.length); + } + + //---------------------------------------------------------------------------- + + /** + *

Append to the toString a float + * array.

+ * + * @param buffer the StringBuffer to populate + * @param fieldName the field name + * @param array the array to add to the toString + * @param fullDetail true for detail, false + * for summary info, null for style decides + */ + public void append(StringBuffer buffer, String fieldName, float[] array, Boolean fullDetail) { + appendFieldStart(buffer, fieldName); + + if (array == null) { + appendNullText(buffer, fieldName); + + } else if (isFullDetail(fullDetail)) { + appendDetail(buffer, fieldName, array); + + } else { + appendSummary(buffer, fieldName, array); + } + + appendFieldEnd(buffer, fieldName); + } + + /** + *

Append to the toString the detail of a + * float array.

+ * + * @param buffer the StringBuffer to populate + * @param fieldName the field name, typically not used as already appended + * @param array the array to add to the toString, + * not null + */ + protected void appendDetail(StringBuffer buffer, String fieldName, float[] array) { + buffer.append(arrayStart); + for (int i = 0; i < array.length; i++) { + if (i > 0) { + buffer.append(arraySeparator); + } + appendDetail(buffer, fieldName, array[i]); + } + buffer.append(arrayEnd); + } + + /** + *

Append to the toString a summary of a + * float array.

+ * + * @param buffer the StringBuffer to populate + * @param fieldName the field name, typically not used as already appended + * @param array the array to add to the toString, + * not null + */ + protected void appendSummary(StringBuffer buffer, String fieldName, float[] array) { + appendSummarySize(buffer, fieldName, array.length); + } + + //---------------------------------------------------------------------------- + + /** + *

Append to the toString a boolean + * array.

+ * + * @param buffer the StringBuffer to populate + * @param fieldName the field name + * @param array the array to add to the toString + * @param fullDetail true for detail, false + * for summary info, null for style decides + */ + public void append(StringBuffer buffer, String fieldName, boolean[] array, Boolean fullDetail) { + appendFieldStart(buffer, fieldName); + + if (array == null) { + appendNullText(buffer, fieldName); + + } else if (isFullDetail(fullDetail)) { + appendDetail(buffer, fieldName, array); + + } else { + appendSummary(buffer, fieldName, array); + } + + appendFieldEnd(buffer, fieldName); + } + + /** + *

Append to the toString the detail of a + * boolean array.

+ * + * @param buffer the StringBuffer to populate + * @param fieldName the field name, typically not used as already appended + * @param array the array to add to the toString, + * not null + */ + protected void appendDetail(StringBuffer buffer, String fieldName, boolean[] array) { + buffer.append(arrayStart); + for (int i = 0; i < array.length; i++) { + if (i > 0) { + buffer.append(arraySeparator); + } + appendDetail(buffer, fieldName, array[i]); + } + buffer.append(arrayEnd); + } + + /** + *

Append to the toString a summary of a + * boolean array.

+ * + * @param buffer the StringBuffer to populate + * @param fieldName the field name, typically not used as already appended + * @param array the array to add to the toString, + * not null + */ + protected void appendSummary(StringBuffer buffer, String fieldName, boolean[] array) { + appendSummarySize(buffer, fieldName, array.length); + } + + //---------------------------------------------------------------------------- + + /** + *

Append to the toString the class name.

+ * + * @param buffer the StringBuffer to populate + * @param object the Object whose name to output + */ + protected void appendClassName(StringBuffer buffer, Object object) { + if (useClassName && object != null) { + register(object); + if (useShortClassName) { + buffer.append(getShortClassName(object.getClass())); + } else { + buffer.append(object.getClass().getName()); + } + } + } + + /** + *

Append the {@link System#identityHashCode(java.lang.Object)}.

+ * + * @param buffer the StringBuffer to populate + * @param object the Object whose id to output + */ + protected void appendIdentityHashCode(StringBuffer buffer, Object object) { + if (this.isUseIdentityHashCode() && object!=null) { + register(object); + buffer.append('@'); + buffer.append(Integer.toHexString(System.identityHashCode(object))); + } + } + + /** + *

Append to the toString the content start.

+ * + * @param buffer the StringBuffer to populate + */ + protected void appendContentStart(StringBuffer buffer) { + buffer.append(contentStart); + } + + /** + *

Append to the toString the content end.

+ * + * @param buffer the StringBuffer to populate + */ + protected void appendContentEnd(StringBuffer buffer) { + buffer.append(contentEnd); + } + + /** + *

Append to the toString an indicator for null.

+ * + *

The default indicator is '<null>'.

+ * + * @param buffer the StringBuffer to populate + * @param fieldName the field name, typically not used as already appended + */ + protected void appendNullText(StringBuffer buffer, String fieldName) { + buffer.append(nullText); + } + + /** + *

Append to the toString the field separator.

+ * + * @param buffer the StringBuffer to populate + */ + protected void appendFieldSeparator(StringBuffer buffer) { + buffer.append(fieldSeparator); + } + + /** + *

Append to the toString the field start.

+ * + * @param buffer the StringBuffer to populate + * @param fieldName the field name + */ + protected void appendFieldStart(StringBuffer buffer, String fieldName) { + if (useFieldNames && fieldName != null) { + buffer.append(fieldName); + buffer.append(fieldNameValueSeparator); + } + } + + /** + *

Append to the toString the field end.

+ * + * @param buffer the StringBuffer to populate + * @param fieldName the field name, typically not used as already appended + */ + protected void appendFieldEnd(StringBuffer buffer, String fieldName) { + appendFieldSeparator(buffer); + } + + /** + *

Append to the toString a size summary.

+ * + *

The size summary is used to summarize the contents of + * Collections, Maps and arrays.

+ * + *

The output consists of a prefix, the passed in size + * and a suffix.

+ * + *

The default format is '<size=n>'.

+ * + * @param buffer the StringBuffer to populate + * @param fieldName the field name, typically not used as already appended + * @param size the size to append + */ + protected void appendSummarySize(StringBuffer buffer, String fieldName, int size) { + buffer.append(sizeStartText); + buffer.append(size); + buffer.append(sizeEndText); + } + + /** + *

Is this field to be output in full detail.

+ * + *

This method converts a detail request into a detail level. + * The calling code may request full detail (true), + * but a subclass might ignore that and always return + * false. The calling code may pass in + * null indicating that it doesn't care about + * the detail level. In this case the default detail level is + * used.

+ * + * @param fullDetailRequest the detail level requested + * @return whether full detail is to be shown + */ + protected boolean isFullDetail(Boolean fullDetailRequest) { + if (fullDetailRequest == null) { + return defaultFullDetail; + } + return fullDetailRequest.booleanValue(); + } + + /** + *

Gets the short class name for a class.

+ * + *

The short class name is the classname excluding + * the package name.

+ * + * @param cls the Class to get the short name of + * @return the short name + */ + protected String getShortClassName(Class cls) { + return ClassUtils.getShortClassName(cls); + } + + // Setters and getters for the customizable parts of the style + // These methods are not expected to be overridden, except to make public + // (They are not public so that immutable subclasses can be written) + //--------------------------------------------------------------------- + + /** + *

Gets whether to use the class name.

+ * + * @return the current useClassName flag + */ + protected boolean isUseClassName() { + return useClassName; + } + + /** + *

Sets whether to use the class name.

+ * + * @param useClassName the new useClassName flag + */ + protected void setUseClassName(boolean useClassName) { + this.useClassName = useClassName; + } + + //--------------------------------------------------------------------- + + /** + *

Gets whether to output short or long class names.

+ * + * @return the current useShortClassName flag + * @since 2.0 + */ + protected boolean isUseShortClassName() { + return useShortClassName; + } + + /** + *

Sets whether to output short or long class names.

+ * + * @param useShortClassName the new useShortClassName flag + * @since 2.0 + */ + protected void setUseShortClassName(boolean useShortClassName) { + this.useShortClassName = useShortClassName; + } + + //--------------------------------------------------------------------- + + /** + *

Gets whether to use the identity hash code.

+ * + * @return the current useIdentityHashCode flag + */ + protected boolean isUseIdentityHashCode() { + return useIdentityHashCode; + } + + /** + *

Sets whether to use the identity hash code.

+ * + * @param useIdentityHashCode the new useIdentityHashCode flag + */ + protected void setUseIdentityHashCode(boolean useIdentityHashCode) { + this.useIdentityHashCode = useIdentityHashCode; + } + + //--------------------------------------------------------------------- + + /** + *

Gets whether to use the field names passed in.

+ * + * @return the current useFieldNames flag + */ + protected boolean isUseFieldNames() { + return useFieldNames; + } + + /** + *

Sets whether to use the field names passed in.

+ * + * @param useFieldNames the new useFieldNames flag + */ + protected void setUseFieldNames(boolean useFieldNames) { + this.useFieldNames = useFieldNames; + } + + //--------------------------------------------------------------------- + + /** + *

Gets whether to use full detail when the caller doesn't + * specify.

+ * + * @return the current defaultFullDetail flag + */ + protected boolean isDefaultFullDetail() { + return defaultFullDetail; + } + + /** + *

Sets whether to use full detail when the caller doesn't + * specify.

+ * + * @param defaultFullDetail the new defaultFullDetail flag + */ + protected void setDefaultFullDetail(boolean defaultFullDetail) { + this.defaultFullDetail = defaultFullDetail; + } + + //--------------------------------------------------------------------- + + /** + *

Gets whether to output array content detail.

+ * + * @return the current array content detail setting + */ + protected boolean isArrayContentDetail() { + return arrayContentDetail; + } + + /** + *

Sets whether to output array content detail.

+ * + * @param arrayContentDetail the new arrayContentDetail flag + */ + protected void setArrayContentDetail(boolean arrayContentDetail) { + this.arrayContentDetail = arrayContentDetail; + } + + //--------------------------------------------------------------------- + + /** + *

Gets the array start text.

+ * + * @return the current array start text + */ + protected String getArrayStart() { + return arrayStart; + } + + /** + *

Sets the array start text.

+ * + *

null is accepted, but will be converted to + * an empty String.

+ * + * @param arrayStart the new array start text + */ + protected void setArrayStart(String arrayStart) { + if (arrayStart == null) { + arrayStart = ""; + } + this.arrayStart = arrayStart; + } + + //--------------------------------------------------------------------- + + /** + *

Gets the array end text.

+ * + * @return the current array end text + */ + protected String getArrayEnd() { + return arrayEnd; + } + + /** + *

Sets the array end text.

+ * + *

null is accepted, but will be converted to + * an empty String.

+ * + * @param arrayEnd the new array end text + */ + protected void setArrayEnd(String arrayEnd) { + if (arrayEnd == null) { + arrayEnd = ""; + } + this.arrayEnd = arrayEnd; + } + + //--------------------------------------------------------------------- + + /** + *

Gets the array separator text.

+ * + * @return the current array separator text + */ + protected String getArraySeparator() { + return arraySeparator; + } + + /** + *

Sets the array separator text.

+ * + *

null is accepted, but will be converted to + * an empty String.

+ * + * @param arraySeparator the new array separator text + */ + protected void setArraySeparator(String arraySeparator) { + if (arraySeparator == null) { + arraySeparator = ""; + } + this.arraySeparator = arraySeparator; + } + + //--------------------------------------------------------------------- + + /** + *

Gets the content start text.

+ * + * @return the current content start text + */ + protected String getContentStart() { + return contentStart; + } + + /** + *

Sets the content start text.

+ * + *

null is accepted, but will be converted to + * an empty String.

+ * + * @param contentStart the new content start text + */ + protected void setContentStart(String contentStart) { + if (contentStart == null) { + contentStart = ""; + } + this.contentStart = contentStart; + } + + //--------------------------------------------------------------------- + + /** + *

Gets the content end text.

+ * + * @return the current content end text + */ + protected String getContentEnd() { + return contentEnd; + } + + /** + *

Sets the content end text.

+ * + *

null is accepted, but will be converted to + * an empty String.

+ * + * @param contentEnd the new content end text + */ + protected void setContentEnd(String contentEnd) { + if (contentEnd == null) { + contentEnd = ""; + } + this.contentEnd = contentEnd; + } + + //--------------------------------------------------------------------- + + /** + *

Gets the field name value separator text.

+ * + * @return the current field name value separator text + */ + protected String getFieldNameValueSeparator() { + return fieldNameValueSeparator; + } + + /** + *

Sets the field name value separator text.

+ * + *

null is accepted, but will be converted to + * an empty String.

+ * + * @param fieldNameValueSeparator the new field name value separator text + */ + protected void setFieldNameValueSeparator(String fieldNameValueSeparator) { + if (fieldNameValueSeparator == null) { + fieldNameValueSeparator = ""; + } + this.fieldNameValueSeparator = fieldNameValueSeparator; + } + + //--------------------------------------------------------------------- + + /** + *

Gets the field separator text.

+ * + * @return the current field separator text + */ + protected String getFieldSeparator() { + return fieldSeparator; + } + + /** + *

Sets the field separator text.

+ * + *

null is accepted, but will be converted to + * an empty String.

+ * + * @param fieldSeparator the new field separator text + */ + protected void setFieldSeparator(String fieldSeparator) { + if (fieldSeparator == null) { + fieldSeparator = ""; + } + this.fieldSeparator = fieldSeparator; + } + + //--------------------------------------------------------------------- + + /** + *

Gets whether the field separator should be added at the start + * of each buffer.

+ * + * @return the fieldSeparatorAtStart flag + * @since 2.0 + */ + protected boolean isFieldSeparatorAtStart() { + return fieldSeparatorAtStart; + } + + /** + *

Sets whether the field separator should be added at the start + * of each buffer.

+ * + * @param fieldSeparatorAtStart the fieldSeparatorAtStart flag + * @since 2.0 + */ + protected void setFieldSeparatorAtStart(boolean fieldSeparatorAtStart) { + this.fieldSeparatorAtStart = fieldSeparatorAtStart; + } + + //--------------------------------------------------------------------- + + /** + *

Gets whether the field separator should be added at the end + * of each buffer.

+ * + * @return fieldSeparatorAtEnd flag + * @since 2.0 + */ + protected boolean isFieldSeparatorAtEnd() { + return fieldSeparatorAtEnd; + } + + /** + *

Sets whether the field separator should be added at the end + * of each buffer.

+ * + * @param fieldSeparatorAtEnd the fieldSeparatorAtEnd flag + * @since 2.0 + */ + protected void setFieldSeparatorAtEnd(boolean fieldSeparatorAtEnd) { + this.fieldSeparatorAtEnd = fieldSeparatorAtEnd; + } + + //--------------------------------------------------------------------- + + /** + *

Gets the text to output when null found.

+ * + * @return the current text to output when null found + */ + protected String getNullText() { + return nullText; + } + + /** + *

Sets the text to output when null found.

+ * + *

null is accepted, but will be converted to + * an empty String.

+ * + * @param nullText the new text to output when null found + */ + protected void setNullText(String nullText) { + if (nullText == null) { + nullText = ""; + } + this.nullText = nullText; + } + + //--------------------------------------------------------------------- + + /** + *

Gets the start text to output when a Collection, + * Map or array size is output.

+ * + *

This is output before the size value.

+ * + * @return the current start of size text + */ + protected String getSizeStartText() { + return sizeStartText; + } + + /** + *

Sets the start text to output when a Collection, + * Map or array size is output.

+ * + *

This is output before the size value.

+ * + *

null is accepted, but will be converted to + * an empty String.

+ * + * @param sizeStartText the new start of size text + */ + protected void setSizeStartText(String sizeStartText) { + if (sizeStartText == null) { + sizeStartText = ""; + } + this.sizeStartText = sizeStartText; + } + + //--------------------------------------------------------------------- + + /** + *

Gets the end text to output when a Collection, + * Map or array size is output.

+ * + *

This is output after the size value.

+ * + * @return the current end of size text + */ + protected String getSizeEndText() { + return sizeEndText; + } + + /** + *

Sets the end text to output when a Collection, + * Map or array size is output.

+ * + *

This is output after the size value.

+ * + *

null is accepted, but will be converted to + * an empty String.

+ * + * @param sizeEndText the new end of size text + */ + protected void setSizeEndText(String sizeEndText) { + if (sizeEndText == null) { + sizeEndText = ""; + } + this.sizeEndText = sizeEndText; + } + + //--------------------------------------------------------------------- + + /** + *

Gets the start text to output when an Object is + * output in summary mode.

+ * + *

This is output before the size value.

+ * + * @return the current start of summary text + */ + protected String getSummaryObjectStartText() { + return summaryObjectStartText; + } + + /** + *

Sets the start text to output when an Object is + * output in summary mode.

+ * + *

This is output before the size value.

+ * + *

null is accepted, but will be converted to + * an empty String.

+ * + * @param summaryObjectStartText the new start of summary text + */ + protected void setSummaryObjectStartText(String summaryObjectStartText) { + if (summaryObjectStartText == null) { + summaryObjectStartText = ""; + } + this.summaryObjectStartText = summaryObjectStartText; + } + + //--------------------------------------------------------------------- + + /** + *

Gets the end text to output when an Object is + * output in summary mode.

+ * + *

This is output after the size value.

+ * + * @return the current end of summary text + */ + protected String getSummaryObjectEndText() { + return summaryObjectEndText; + } + + /** + *

Sets the end text to output when an Object is + * output in summary mode.

+ * + *

This is output after the size value.

+ * + *

null is accepted, but will be converted to + * an empty String.

+ * + * @param summaryObjectEndText the new end of summary text + */ + protected void setSummaryObjectEndText(String summaryObjectEndText) { + if (summaryObjectEndText == null) { + summaryObjectEndText = ""; + } + this.summaryObjectEndText = summaryObjectEndText; + } + + //---------------------------------------------------------------------------- + + /** + *

Default ToStringStyle.

+ * + *

This is an inner class rather than using + * StandardToStringStyle to ensure its immutability.

+ */ + private static final class DefaultToStringStyle extends ToStringStyle { + + /** + * Required for serialization support. + * + * @see java.io.Serializable + */ + private static final long serialVersionUID = 1L; + + /** + *

Constructor.

+ * + *

Use the static constant rather than instantiating.

+ */ + DefaultToStringStyle() { + super(); + } + + /** + *

Ensure Singleton after serialization.

+ * + * @return the singleton + */ + private Object readResolve() { + return ToStringStyle.DEFAULT_STYLE; + } + + } + + //---------------------------------------------------------------------------- + + /** + *

ToStringStyle that does not print out + * the field names.

+ * + *

This is an inner class rather than using + * StandardToStringStyle to ensure its immutability. + */ + private static final class NoFieldNameToStringStyle extends ToStringStyle { + + private static final long serialVersionUID = 1L; + + /** + *

Constructor.

+ * + *

Use the static constant rather than instantiating.

+ */ + NoFieldNameToStringStyle() { + super(); + this.setUseFieldNames(false); + } + + /** + *

Ensure Singleton after serialization.

+ * + * @return the singleton + */ + private Object readResolve() { + return ToStringStyle.NO_FIELD_NAMES_STYLE; + } + + } + + //---------------------------------------------------------------------------- + + /** + *

ToStringStyle that prints out the short + * class name and no identity hashcode.

+ * + *

This is an inner class rather than using + * StandardToStringStyle to ensure its immutability.

+ */ + private static final class ShortPrefixToStringStyle extends ToStringStyle { + + private static final long serialVersionUID = 1L; + + /** + *

Constructor.

+ * + *

Use the static constant rather than instantiating.

+ */ + ShortPrefixToStringStyle() { + super(); + this.setUseShortClassName(true); + this.setUseIdentityHashCode(false); + } + + /** + *

Ensure Singleton after serialization.

+ * @return the singleton + */ + private Object readResolve() { + return ToStringStyle.SHORT_PREFIX_STYLE; + } + + } + + /** + *

ToStringStyle that does not print out the + * classname, identity hashcode, content start or field name.

+ * + *

This is an inner class rather than using + * StandardToStringStyle to ensure its immutability.

+ */ + private static final class SimpleToStringStyle extends ToStringStyle { + + private static final long serialVersionUID = 1L; + + /** + *

Constructor.

+ * + *

Use the static constant rather than instantiating.

+ */ + SimpleToStringStyle() { + super(); + this.setUseClassName(false); + this.setUseIdentityHashCode(false); + this.setUseFieldNames(false); + this.setContentStart(""); + this.setContentEnd(""); + } + + /** + *

Ensure Singleton after serialization.

+ * @return the singleton + */ + private Object readResolve() { + return ToStringStyle.SIMPLE_STYLE; + } + + } + + //---------------------------------------------------------------------------- + + /** + *

ToStringStyle that outputs on multiple lines.

+ * + *

This is an inner class rather than using + * StandardToStringStyle to ensure its immutability.

+ */ + private static final class MultiLineToStringStyle extends ToStringStyle { + + private static final long serialVersionUID = 1L; + + /** + *

Constructor.

+ * + *

Use the static constant rather than instantiating.

+ */ + MultiLineToStringStyle() { + super(); + this.setContentStart("["); + this.setFieldSeparator(SystemUtils.LINE_SEPARATOR + " "); + this.setFieldSeparatorAtStart(true); + this.setContentEnd(SystemUtils.LINE_SEPARATOR + "]"); + } + + /** + *

Ensure Singleton after serialization.

+ * + * @return the singleton + */ + private Object readResolve() { + return ToStringStyle.MULTI_LINE_STYLE; + } + + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/builder/ToStringStyleConcurrencyTest.java b/ApacheCommonsLang/org/apache/commons/lang3/builder/ToStringStyleConcurrencyTest.java new file mode 100644 index 0000000..99a6021 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/builder/ToStringStyleConcurrencyTest.java @@ -0,0 +1,110 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.lang3.builder; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +import org.junit.Test; + +/** + * Tests concurrent access for the default {@link ToStringStyle}. + *

+ * The {@link ToStringStyle} class includes a registry to avoid infinite loops for objects with circular references. We + * want to make sure that we do not get concurrency exceptions accessing this registry. + *

+ *

+ * This test passes but only tests one aspect of the issue. + *

+ * + * @see [LANG-762] Handle or document ReflectionToStringBuilder + * and ToStringBuilder for collections that are not thread safe + * @since 3.1 + * @version $Id: ToStringStyleConcurrencyTest.java 1309977 2012-04-05 17:53:39Z ggregory $ + */ +public class ToStringStyleConcurrencyTest { + + static class CollectionHolder> { + T collection; + + CollectionHolder(T collection) { + this.collection = collection; + } + } + + private static final List LIST; + private static final int LIST_SIZE = 100000; + private static final int REPEAT = 100; + + static { + LIST = new ArrayList(LIST_SIZE); + for (int i = 0; i < LIST_SIZE; i++) { + LIST.add(Integer.valueOf(i)); + } + } + + @Test + public void testLinkedList() throws InterruptedException, ExecutionException { + this.testConcurrency(new CollectionHolder>(new LinkedList())); + } + + @Test + public void testArrayList() throws InterruptedException, ExecutionException { + this.testConcurrency(new CollectionHolder>(new ArrayList())); + } + + @Test + public void testCopyOnWriteArrayList() throws InterruptedException, ExecutionException { + this.testConcurrency(new CollectionHolder>(new CopyOnWriteArrayList())); + } + + private void testConcurrency(final CollectionHolder> holder) throws InterruptedException, + ExecutionException { + final List list = holder.collection; + // make a big array that takes a long time to toString() + list.addAll(LIST); + // Create a thread pool with two threads to cause the most contention on the underlying resource. + final ExecutorService threadPool = Executors.newFixedThreadPool(2); + // Consumes toStrings + Callable consumer = new Callable() { + @Override + public Integer call() { + for (int i = 0; i < REPEAT; i++) { + // Calls ToStringStyle + new ToStringBuilder(holder).append(holder.collection); + } + return REPEAT; + } + }; + Collection> tasks = new ArrayList>(); + tasks.add(consumer); + tasks.add(consumer); + final List> futures = threadPool.invokeAll(tasks); + for (Future future : futures) { + future.get(); + } + } +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/builder/ToStringStyleTest.java b/ApacheCommonsLang/org/apache/commons/lang3/builder/ToStringStyleTest.java new file mode 100644 index 0000000..df29cb6 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/builder/ToStringStyleTest.java @@ -0,0 +1,138 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.builder; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +/** + * Test case for ToStringStyle. + * + * @version $Id: ToStringStyleTest.java 1185695 2011-10-18 14:47:48Z ggregory $ + */ +public class ToStringStyleTest { + + private static class ToStringStyleImpl extends ToStringStyle { + } + + //----------------------------------------------------------------------- + @Test + public void testSetArrayStart() { + ToStringStyle style = new ToStringStyleImpl(); + style.setArrayStart(null); + assertEquals("", style.getArrayStart()); + } + + @Test + public void testSetArrayEnd() { + ToStringStyle style = new ToStringStyleImpl(); + style.setArrayEnd(null); + assertEquals("", style.getArrayEnd()); + } + + @Test + public void testSetArraySeparator() { + ToStringStyle style = new ToStringStyleImpl(); + style.setArraySeparator(null); + assertEquals("", style.getArraySeparator()); + } + + @Test + public void testSetContentStart() { + ToStringStyle style = new ToStringStyleImpl(); + style.setContentStart(null); + assertEquals("", style.getContentStart()); + } + + @Test + public void testSetContentEnd() { + ToStringStyle style = new ToStringStyleImpl(); + style.setContentEnd(null); + assertEquals("", style.getContentEnd()); + } + + @Test + public void testSetFieldNameValueSeparator() { + ToStringStyle style = new ToStringStyleImpl(); + style.setFieldNameValueSeparator(null); + assertEquals("", style.getFieldNameValueSeparator()); + } + + @Test + public void testSetFieldSeparator() { + ToStringStyle style = new ToStringStyleImpl(); + style.setFieldSeparator(null); + assertEquals("", style.getFieldSeparator()); + } + + @Test + public void testSetNullText() { + ToStringStyle style = new ToStringStyleImpl(); + style.setNullText(null); + assertEquals("", style.getNullText()); + } + + @Test + public void testSetSizeStartText() { + ToStringStyle style = new ToStringStyleImpl(); + style.setSizeStartText(null); + assertEquals("", style.getSizeStartText()); + } + + @Test + public void testSetSizeEndText() { + ToStringStyle style = new ToStringStyleImpl(); + style.setSizeEndText(null); + assertEquals("", style.getSizeEndText()); + } + + @Test + public void testSetSummaryObjectStartText() { + ToStringStyle style = new ToStringStyleImpl(); + style.setSummaryObjectStartText(null); + assertEquals("", style.getSummaryObjectStartText()); + } + + @Test + public void testSetSummaryObjectEndText() { + ToStringStyle style = new ToStringStyleImpl(); + style.setSummaryObjectEndText(null); + assertEquals("", style.getSummaryObjectEndText()); + } + + /** + * An object used to test {@link ToStringStyle}. + * + */ + static class Person { + /** + * Test String field. + */ + String name; + + /** + * Test integer field. + */ + int age; + + /** + * Test boolean field. + */ + boolean smoker; + } +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/builder/package.html b/ApacheCommonsLang/org/apache/commons/lang3/builder/package.html new file mode 100644 index 0000000..6c85d68 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/builder/package.html @@ -0,0 +1,28 @@ + + + +Assists in creating consistent equals(Object), toString(), +hashCode(), and compareTo(Object) methods. +@see java.lang.Object#equals(Object) +@see java.lang.Object#toString() +@see java.lang.Object#hashCode() +@see java.lang.Comparable#compareTo(Object) +@since 1.0 +

These classes are not thread-safe.

+ + diff --git a/ApacheCommonsLang/org/apache/commons/lang3/concurrent/AbstractConcurrentInitializerTest.java b/ApacheCommonsLang/org/apache/commons/lang3/concurrent/AbstractConcurrentInitializerTest.java new file mode 100644 index 0000000..2671ca8 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/concurrent/AbstractConcurrentInitializerTest.java @@ -0,0 +1,115 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.concurrent; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import java.util.concurrent.CountDownLatch; + +import org.junit.Test; + +/** + *

+ * An abstract base class for tests of concrete {@code ConcurrentInitializer} + * implementations. + *

+ *

+ * This class provides some basic tests for initializer implementations. Derived + * class have to create a {@link ConcurrentInitializer} object on which the + * tests are executed. + *

+ * + * @version $Id: AbstractConcurrentInitializerTest.java 1088899 2011-04-05 05:31:27Z bayard $ + */ +public abstract class AbstractConcurrentInitializerTest { + /** + * Tests a simple invocation of the get() method. + */ + @Test + public void testGet() throws ConcurrentException { + assertNotNull("No managed object", createInitializer().get()); + } + + /** + * Tests whether sequential get() invocations always return the same + * instance. + */ + @Test + public void testGetMultipleTimes() throws ConcurrentException { + ConcurrentInitializer initializer = createInitializer(); + Object obj = initializer.get(); + for (int i = 0; i < 10; i++) { + assertEquals("Got different object at " + i, obj, initializer.get()); + } + } + + /** + * Tests whether get() can be invoked from multiple threads concurrently. + * Always the same object should be returned. + */ + @Test + public void testGetConcurrent() throws ConcurrentException, + InterruptedException { + final ConcurrentInitializer initializer = createInitializer(); + final int threadCount = 20; + final CountDownLatch startLatch = new CountDownLatch(1); + class GetThread extends Thread { + Object object; + + @Override + public void run() { + try { + // wait until all threads are ready for maximum parallelism + startLatch.await(); + // access the initializer + object = initializer.get(); + } catch (InterruptedException iex) { + // ignore + } catch (ConcurrentException cex) { + object = cex; + } + } + } + + GetThread[] threads = new GetThread[threadCount]; + for (int i = 0; i < threadCount; i++) { + threads[i] = new GetThread(); + threads[i].start(); + } + + // fire all threads and wait until they are ready + startLatch.countDown(); + for (Thread t : threads) { + t.join(); + } + + // check results + Object managedObject = initializer.get(); + for (GetThread t : threads) { + assertEquals("Wrong object", managedObject, t.object); + } + } + + /** + * Creates the {@link ConcurrentInitializer} object to be tested. This + * method is called whenever the test fixture needs to be obtained. + * + * @return the initializer object to be tested + */ + protected abstract ConcurrentInitializer createInitializer(); +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/concurrent/AtomicInitializer.java b/ApacheCommonsLang/org/apache/commons/lang3/concurrent/AtomicInitializer.java new file mode 100644 index 0000000..faf89d4 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/concurrent/AtomicInitializer.java @@ -0,0 +1,107 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.concurrent; + +import java.util.concurrent.atomic.AtomicReference; + +/** + *

+ * A specialized implementation of the {@code ConcurrentInitializer} interface + * based on an {@link AtomicReference} variable. + *

+ *

+ * This class maintains a member field of type {@code AtomicReference}. It + * implements the following algorithm to create and initialize an object in its + * {@link #get()} method: + *

    + *
  • First it is checked whether the {@code AtomicReference} variable contains + * already a value. If this is the case, the value is directly returned.
  • + *
  • Otherwise the {@link #initialize()} method is called. This method must be + * defined in concrete subclasses to actually create the managed object.
  • + *
  • After the object was created by {@link #initialize()} it is checked + * whether the {@code AtomicReference} variable is still undefined. This has to + * be done because in the meantime another thread may have initialized the + * object. If the reference is still empty, the newly created object is stored + * in it and returned by this method.
  • + *
  • Otherwise the value stored in the {@code AtomicReference} is returned.
  • + *
+ *

+ *

+ * Because atomic variables are used this class does not need any + * synchronization. So there is no danger of deadlock, and access to the managed + * object is efficient. However, if multiple threads access the {@code + * AtomicInitializer} object before it has been initialized almost at the same + * time, it can happen that {@link #initialize()} is called multiple times. The + * algorithm outlined above guarantees that {@link #get()} always returns the + * same object though. + *

+ *

+ * Compared with the {@link LazyInitializer} class, this class can be more + * efficient because it does not need synchronization. The drawback is that the + * {@link #initialize()} method can be called multiple times which may be + * problematic if the creation of the managed object is expensive. As a rule of + * thumb this initializer implementation is preferable if there are not too many + * threads involved and the probability that multiple threads access an + * uninitialized object is small. If there is high parallelism, + * {@link LazyInitializer} is more appropriate. + *

+ * + * @since 3.0 + * @version $Id: AtomicInitializer.java 1309977 2012-04-05 17:53:39Z ggregory $ + * @param the type of the object managed by this initializer class + */ +public abstract class AtomicInitializer implements ConcurrentInitializer { + /** Holds the reference to the managed object. */ + private final AtomicReference reference = new AtomicReference(); + + /** + * Returns the object managed by this initializer. The object is created if + * it is not available yet and stored internally. This method always returns + * the same object. + * + * @return the object created by this {@code AtomicInitializer} + * @throws ConcurrentException if an error occurred during initialization of + * the object + */ + @Override + public T get() throws ConcurrentException { + T result = reference.get(); + + if (result == null) { + result = initialize(); + if (!reference.compareAndSet(null, result)) { + // another thread has initialized the reference + result = reference.get(); + } + } + + return result; + } + + /** + * Creates and initializes the object managed by this {@code + * AtomicInitializer}. This method is called by {@link #get()} when the + * managed object is not available yet. An implementation can focus on the + * creation of the object. No synchronization is needed, as this is already + * handled by {@code get()}. As stated by the class comment, it is possible + * that this method is called multiple times. + * + * @return the managed data object + * @throws ConcurrentException if an error occurs during object creation + */ + protected abstract T initialize() throws ConcurrentException; +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/concurrent/AtomicInitializerTest.java b/ApacheCommonsLang/org/apache/commons/lang3/concurrent/AtomicInitializerTest.java new file mode 100644 index 0000000..2d8cfc6 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/concurrent/AtomicInitializerTest.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.concurrent; + +/** + * Test class for {@code AtomicInitializer}. + * + * @version $Id: AtomicInitializerTest.java 1088899 2011-04-05 05:31:27Z bayard $ + */ +public class AtomicInitializerTest extends AbstractConcurrentInitializerTest { + /** + * Returns the initializer to be tested. + * + * @return the {@code AtomicInitializer} + */ + @Override + protected ConcurrentInitializer createInitializer() { + return new AtomicInitializer() { + @Override + protected Object initialize() throws ConcurrentException { + return new Object(); + } + }; + } +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/concurrent/AtomicSafeInitializer.java b/ApacheCommonsLang/org/apache/commons/lang3/concurrent/AtomicSafeInitializer.java new file mode 100644 index 0000000..a895361 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/concurrent/AtomicSafeInitializer.java @@ -0,0 +1,97 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.concurrent; + +import java.util.concurrent.atomic.AtomicReference; + +/** + *

+ * A specialized {@code ConcurrentInitializer} implementation which is similar + * to {@link AtomicInitializer}, but ensures that the {@link #initialize()} + * method is called only once. + *

+ *

+ * As {@link AtomicInitializer} this class is based on atomic variables, so it + * can create an object under concurrent access without synchronization. + * However, it implements an additional check to guarantee that the + * {@link #initialize()} method which actually creates the object cannot be + * called multiple times. + *

+ *

+ * Because of this additional check this implementation is slightly less + * efficient than {@link AtomicInitializer}, but if the object creation in the + * {@code initialize()} method is expensive or if multiple invocations of + * {@code initialize()} are problematic, it is the better alternative. + *

+ *

+ * From its semantics this class has the same properties as + * {@link LazyInitializer}. It is a "save" implementation of the lazy + * initializer pattern. Comparing both classes in terms of efficiency is + * difficult because which one is faster depends on multiple factors. Because + * {@code AtomicSafeInitializer} does not use synchronization at all it probably + * outruns {@link LazyInitializer}, at least under low or moderate concurrent + * access. Developers should run their own benchmarks on the expected target + * platform to decide which implementation is suitable for their specific use + * case. + *

+ * + * @since 3.0 + * @version $Id: AtomicSafeInitializer.java 1309977 2012-04-05 17:53:39Z ggregory $ + * @param the type of the object managed by this initializer class + */ +public abstract class AtomicSafeInitializer implements + ConcurrentInitializer { + /** A guard which ensures that initialize() is called only once. */ + private final AtomicReference> factory = + new AtomicReference>(); + + /** Holds the reference to the managed object. */ + private final AtomicReference reference = new AtomicReference(); + + /** + * Get (and initialize, if not initialized yet) the required object + * + * @return lazily initialized object + * @throws ConcurrentException if the initialization of the object causes an + * exception + */ + @Override + public final T get() throws ConcurrentException { + T result; + + while ((result = reference.get()) == null) { + if (factory.compareAndSet(null, this)) { + reference.set(initialize()); + } + } + + return result; + } + + /** + * Creates and initializes the object managed by this + * {@code AtomicInitializer}. This method is called by {@link #get()} when + * the managed object is not available yet. An implementation can focus on + * the creation of the object. No synchronization is needed, as this is + * already handled by {@code get()}. This method is guaranteed to be called + * only once. + * + * @return the managed data object + * @throws ConcurrentException if an error occurs during object creation + */ + protected abstract T initialize() throws ConcurrentException; +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/concurrent/AtomicSafeInitializerTest.java b/ApacheCommonsLang/org/apache/commons/lang3/concurrent/AtomicSafeInitializerTest.java new file mode 100644 index 0000000..85feca5 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/concurrent/AtomicSafeInitializerTest.java @@ -0,0 +1,78 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.concurrent; + +import static org.junit.Assert.assertEquals; + +import java.util.concurrent.atomic.AtomicInteger; + +import org.junit.Before; +import org.junit.Test; + +/** + * Test class for {@code AtomicSafeInitializer}. + * + * @version $Id: AtomicSafeInitializerTest.java 1088899 2011-04-05 05:31:27Z bayard $ + */ +public class AtomicSafeInitializerTest extends + AbstractConcurrentInitializerTest { + /** The instance to be tested. */ + private AtomicSafeInitializerTestImpl initializer; + + @Before + public void setUp() throws Exception { + initializer = new AtomicSafeInitializerTestImpl(); + } + + /** + * Returns the initializer to be tested. + * + * @return the {@code AtomicSafeInitializer} under test + */ + @Override + protected ConcurrentInitializer createInitializer() { + return initializer; + } + + /** + * Tests that initialize() is called only once. + */ + @Test + public void testNumberOfInitializeInvocations() throws ConcurrentException, + InterruptedException { + testGetConcurrent(); + assertEquals("Wrong number of invocations", 1, + initializer.initCounter.get()); + } + + /** + * A concrete test implementation of {@code AtomicSafeInitializer}. This + * implementation also counts the number of invocations of the initialize() + * method. + */ + private static class AtomicSafeInitializerTestImpl extends + AtomicSafeInitializer { + /** A counter for initialize() invocations. */ + final AtomicInteger initCounter = new AtomicInteger(); + + @Override + protected Object initialize() throws ConcurrentException { + initCounter.incrementAndGet(); + return new Object(); + } + } +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/concurrent/BackgroundInitializer.java b/ApacheCommonsLang/org/apache/commons/lang3/concurrent/BackgroundInitializer.java new file mode 100644 index 0000000..30490fb --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/concurrent/BackgroundInitializer.java @@ -0,0 +1,335 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.concurrent; + +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; + +/** + *

+ * A class that allows complex initialization operations in a background task. + *

+ *

+ * Applications often have to do some expensive initialization steps when they + * are started, e.g. constructing a connection to a database, reading a + * configuration file, etc. Doing these things in parallel can enhance + * performance as the CPU load can be improved. However, when access to the + * resources initialized in a background thread is actually required, + * synchronization has to be performed to ensure that their initialization is + * complete. + *

+ *

+ * This abstract base class provides support for this use case. A concrete + * subclass must implement the {@link #initialize()} method. Here an arbitrary + * initialization can be implemented, and a result object can be returned. With + * this method in place the basic usage of this class is as follows (where + * {@code MyBackgroundInitializer} is a concrete subclass): + * + *

+ * MyBackgroundInitializer initializer = new MyBackgroundInitializer();
+ * initializer.start();
+ * // Now do some other things. Initialization runs in a parallel thread
+ * ...
+ * // Wait for the end of initialization and access the result object
+ * Object result = initializer.get();
+ * 
+ * + *

+ *

+ * After the construction of a {@code BackgroundInitializer} object its + * {@link #start()} method has to be called. This starts the background + * processing. The application can now continue to do other things. When it + * needs access to the object produced by the {@code BackgroundInitializer} it + * calls its {@link #get()} method. If initialization is already complete, + * {@link #get()} returns the result object immediately. Otherwise it blocks + * until the result object is fully constructed. + *

+ *

+ * {@code BackgroundInitializer} is a thin wrapper around a {@code Future} + * object and uses an {@code ExecutorService} for running the background + * initialization task. It is possible to pass in an {@code ExecutorService} at + * construction time or set one using {@code setExternalExecutor()} before + * {@code start()} was called. Then this object is used to spawn the background + * task. If no {@code ExecutorService} has been provided, {@code + * BackgroundInitializer} creates a temporary {@code ExecutorService} and + * destroys it when initialization is complete. + *

+ *

+ * The methods provided by {@code BackgroundInitializer} provide for minimal + * interaction with the wrapped {@code Future} object. It is also possible to + * obtain the {@code Future} object directly. Then the enhanced functionality + * offered by {@code Future} can be used, e.g. to check whether the background + * operation is complete or to cancel the operation. + *

+ * + * @since 3.0 + * @version $Id: BackgroundInitializer.java 1309977 2012-04-05 17:53:39Z ggregory $ + * @param the type of the object managed by this initializer class + */ +public abstract class BackgroundInitializer implements + ConcurrentInitializer { + /** The external executor service for executing tasks. */ + private ExecutorService externalExecutor; + + /** A reference to the executor service that is actually used. */ + private ExecutorService executor; + + /** Stores the handle to the background task. */ + private Future future; + + /** + * Creates a new instance of {@code BackgroundInitializer}. No external + * {@code ExecutorService} is used. + */ + protected BackgroundInitializer() { + this(null); + } + + /** + * Creates a new instance of {@code BackgroundInitializer} and initializes + * it with the given {@code ExecutorService}. If the {@code ExecutorService} + * is not null, the background task for initializing this object will be + * scheduled at this service. Otherwise a new temporary {@code + * ExecutorService} is created. + * + * @param exec an external {@code ExecutorService} to be used for task + * execution + */ + protected BackgroundInitializer(ExecutorService exec) { + setExternalExecutor(exec); + } + + /** + * Returns the external {@code ExecutorService} to be used by this class. + * + * @return the {@code ExecutorService} + */ + public final synchronized ExecutorService getExternalExecutor() { + return externalExecutor; + } + + /** + * Returns a flag whether this {@code BackgroundInitializer} has already + * been started. + * + * @return a flag whether the {@link #start()} method has already been + * called + */ + public synchronized boolean isStarted() { + return future != null; + } + + /** + * Sets an {@code ExecutorService} to be used by this class. The {@code + * ExecutorService} passed to this method is used for executing the + * background task. Thus it is possible to re-use an already existing + * {@code ExecutorService} or to use a specially configured one. If no + * {@code ExecutorService} is set, this instance creates a temporary one and + * destroys it after background initialization is complete. Note that this + * method must be called before {@link #start()}; otherwise an exception is + * thrown. + * + * @param externalExecutor the {@code ExecutorService} to be used + * @throws IllegalStateException if this initializer has already been + * started + */ + public final synchronized void setExternalExecutor( + ExecutorService externalExecutor) { + if (isStarted()) { + throw new IllegalStateException( + "Cannot set ExecutorService after start()!"); + } + + this.externalExecutor = externalExecutor; + } + + /** + * Starts the background initialization. With this method the initializer + * becomes active and invokes the {@link #initialize()} method in a + * background task. A {@code BackgroundInitializer} can be started exactly + * once. The return value of this method determines whether the start was + * successful: only the first invocation of this method returns true, + * following invocations will return false. + * + * @return a flag whether the initializer could be started successfully + */ + public synchronized boolean start() { + // Not yet started? + if (!isStarted()) { + + // Determine the executor to use and whether a temporary one has to + // be created + ExecutorService tempExec; + executor = getExternalExecutor(); + if (executor == null) { + executor = tempExec = createExecutor(); + } else { + tempExec = null; + } + + future = executor.submit(createTask(tempExec)); + + return true; + } + + return false; + } + + /** + * Returns the result of the background initialization. This method blocks + * until initialization is complete. If the background processing caused a + * runtime exception, it is directly thrown by this method. Checked + * exceptions, including {@code InterruptedException} are wrapped in a + * {@link ConcurrentException}. Calling this method before {@link #start()} + * was called causes an {@code IllegalStateException} exception to be + * thrown. + * + * @return the object produced by this initializer + * @throws ConcurrentException if a checked exception occurred during + * background processing + * @throws IllegalStateException if {@link #start()} has not been called + */ + @Override + public T get() throws ConcurrentException { + try { + return getFuture().get(); + } catch (ExecutionException execex) { + ConcurrentUtils.handleCause(execex); + return null; // should not be reached + } catch (InterruptedException iex) { + // reset interrupted state + Thread.currentThread().interrupt(); + throw new ConcurrentException(iex); + } + } + + /** + * Returns the {@code Future} object that was created when {@link #start()} + * was called. Therefore this method can only be called after {@code + * start()}. + * + * @return the {@code Future} object wrapped by this initializer + * @throws IllegalStateException if {@link #start()} has not been called + */ + public synchronized Future getFuture() { + if (future == null) { + throw new IllegalStateException("start() must be called first!"); + } + + return future; + } + + /** + * Returns the {@code ExecutorService} that is actually used for executing + * the background task. This method can be called after {@link #start()} + * (before {@code start()} it returns null). If an external executor + * was set, this is also the active executor. Otherwise this method returns + * the temporary executor that was created by this object. + * + * @return the {@code ExecutorService} for executing the background task + */ + protected synchronized final ExecutorService getActiveExecutor() { + return executor; + } + + /** + * Returns the number of background tasks to be created for this + * initializer. This information is evaluated when a temporary {@code + * ExecutorService} is created. This base implementation returns 1. Derived + * classes that do more complex background processing can override it. This + * method is called from a synchronized block by the {@link #start()} + * method. Therefore overriding methods should be careful with obtaining + * other locks and return as fast as possible. + * + * @return the number of background tasks required by this initializer + */ + protected int getTaskCount() { + return 1; + } + + /** + * Performs the initialization. This method is called in a background task + * when this {@code BackgroundInitializer} is started. It must be + * implemented by a concrete subclass. An implementation is free to perform + * arbitrary initialization. The object returned by this method can be + * queried using the {@link #get()} method. + * + * @return a result object + * @throws Exception if an error occurs + */ + protected abstract T initialize() throws Exception; + + /** + * Creates a task for the background initialization. The {@code Callable} + * object returned by this method is passed to the {@code ExecutorService}. + * This implementation returns a task that invokes the {@link #initialize()} + * method. If a temporary {@code ExecutorService} is used, it is destroyed + * at the end of the task. + * + * @param execDestroy the {@code ExecutorService} to be destroyed by the + * task + * @return a task for the background initialization + */ + private Callable createTask(ExecutorService execDestroy) { + return new InitializationTask(execDestroy); + } + + /** + * Creates the {@code ExecutorService} to be used. This method is called if + * no {@code ExecutorService} was provided at construction time. + * + * @return the {@code ExecutorService} to be used + */ + private ExecutorService createExecutor() { + return Executors.newFixedThreadPool(getTaskCount()); + } + + private class InitializationTask implements Callable { + /** Stores the executor service to be destroyed at the end. */ + private final ExecutorService execFinally; + + /** + * Creates a new instance of {@code InitializationTask} and initializes + * it with the {@code ExecutorService} to be destroyed at the end. + * + * @param exec the {@code ExecutorService} + */ + public InitializationTask(ExecutorService exec) { + execFinally = exec; + } + + /** + * Initiates initialization and returns the result. + * + * @return the result object + * @throws Exception if an error occurs + */ + @Override + public T call() throws Exception { + try { + return initialize(); + } finally { + if (execFinally != null) { + execFinally.shutdown(); + } + } + } + } +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/concurrent/BackgroundInitializerTest.java b/ApacheCommonsLang/org/apache/commons/lang3/concurrent/BackgroundInitializerTest.java new file mode 100644 index 0000000..b98a8c5 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/concurrent/BackgroundInitializerTest.java @@ -0,0 +1,294 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.concurrent; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; + +import junit.framework.TestCase; + +public class BackgroundInitializerTest extends TestCase { + /** + * Helper method for checking whether the initialize() method was correctly + * called. start() must already have been invoked. + * + * @param init the initializer to test + */ + private void checkInitialize(BackgroundInitializerTestImpl init) { + try { + Integer result = init.get(); + assertEquals("Wrong result", 1, result.intValue()); + assertEquals("Wrong number of invocations", 1, init.initializeCalls); + assertNotNull("No future", init.getFuture()); + } catch (ConcurrentException cex) { + fail("Unexpected exception: " + cex); + } + } + + /** + * Tests whether initialize() is invoked. + */ + public void testInitialize() { + BackgroundInitializerTestImpl init = new BackgroundInitializerTestImpl(); + init.start(); + checkInitialize(init); + } + + /** + * Tries to obtain the executor before start(). It should not have been + * initialized yet. + */ + public void testGetActiveExecutorBeforeStart() { + BackgroundInitializerTestImpl init = new BackgroundInitializerTestImpl(); + assertNull("Got an executor", init.getActiveExecutor()); + } + + /** + * Tests whether an external executor is correctly detected. + */ + public void testGetActiveExecutorExternal() { + ExecutorService exec = Executors.newSingleThreadExecutor(); + try { + BackgroundInitializerTestImpl init = new BackgroundInitializerTestImpl( + exec); + init.start(); + assertSame("Wrong executor", exec, init.getActiveExecutor()); + checkInitialize(init); + } finally { + exec.shutdown(); + } + } + + /** + * Tests getActiveExecutor() for a temporary executor. + */ + public void testGetActiveExecutorTemp() { + BackgroundInitializerTestImpl init = new BackgroundInitializerTestImpl(); + init.start(); + assertNotNull("No active executor", init.getActiveExecutor()); + checkInitialize(init); + } + + /** + * Tests the execution of the background task if a temporary executor has to + * be created. + */ + public void testInitializeTempExecutor() { + BackgroundInitializerTestImpl init = new BackgroundInitializerTestImpl(); + assertTrue("Wrong result of start()", init.start()); + checkInitialize(init); + assertTrue("Executor not shutdown", init.getActiveExecutor() + .isShutdown()); + } + + /** + * Tests whether an external executor can be set using the + * setExternalExecutor() method. + */ + public void testSetExternalExecutor() throws Exception { + ExecutorService exec = Executors.newCachedThreadPool(); + try { + BackgroundInitializerTestImpl init = new BackgroundInitializerTestImpl(); + init.setExternalExecutor(exec); + assertEquals("Wrong executor service", exec, init + .getExternalExecutor()); + assertTrue("Wrong result of start()", init.start()); + assertSame("Wrong active executor", exec, init.getActiveExecutor()); + checkInitialize(init); + assertFalse("Executor was shutdown", exec.isShutdown()); + } finally { + exec.shutdown(); + } + } + + /** + * Tests that setting an executor after start() causes an exception. + */ + public void testSetExternalExecutorAfterStart() throws ConcurrentException { + BackgroundInitializerTestImpl init = new BackgroundInitializerTestImpl(); + init.start(); + try { + init.setExternalExecutor(Executors.newSingleThreadExecutor()); + fail("Could set executor after start()!"); + } catch (IllegalStateException istex) { + init.get(); + } + } + + /** + * Tests invoking start() multiple times. Only the first invocation should + * have an effect. + */ + public void testStartMultipleTimes() { + BackgroundInitializerTestImpl init = new BackgroundInitializerTestImpl(); + assertTrue("Wrong result for start()", init.start()); + for (int i = 0; i < 10; i++) { + assertFalse("Could start again", init.start()); + } + checkInitialize(init); + } + + /** + * Tests calling get() before start(). This should cause an exception. + */ + public void testGetBeforeStart() throws ConcurrentException { + BackgroundInitializerTestImpl init = new BackgroundInitializerTestImpl(); + try { + init.get(); + fail("Could call get() before start()!"); + } catch (IllegalStateException istex) { + // ok + } + } + + /** + * Tests the get() method if background processing causes a runtime + * exception. + */ + public void testGetRuntimeException() throws Exception { + BackgroundInitializerTestImpl init = new BackgroundInitializerTestImpl(); + RuntimeException rex = new RuntimeException(); + init.ex = rex; + init.start(); + try { + init.get(); + fail("Exception not thrown!"); + } catch (Exception ex) { + assertEquals("Runtime exception not thrown", rex, ex); + } + } + + /** + * Tests the get() method if background processing causes a checked + * exception. + */ + public void testGetCheckedException() throws Exception { + BackgroundInitializerTestImpl init = new BackgroundInitializerTestImpl(); + Exception ex = new Exception(); + init.ex = ex; + init.start(); + try { + init.get(); + fail("Exception not thrown!"); + } catch (ConcurrentException cex) { + assertEquals("Exception not thrown", ex, cex.getCause()); + } + } + + /** + * Tests the get() method if waiting for the initialization is interrupted. + */ + public void testGetInterruptedException() throws Exception { + ExecutorService exec = Executors.newSingleThreadExecutor(); + final BackgroundInitializerTestImpl init = new BackgroundInitializerTestImpl( + exec); + final CountDownLatch latch1 = new CountDownLatch(1); + init.shouldSleep = true; + init.start(); + final AtomicReference iex = new AtomicReference(); + Thread getThread = new Thread() { + @Override + public void run() { + try { + init.get(); + } catch (ConcurrentException cex) { + if (cex.getCause() instanceof InterruptedException) { + iex.set((InterruptedException) cex.getCause()); + } + } finally { + assertTrue("Thread not interrupted", isInterrupted()); + latch1.countDown(); + } + } + }; + getThread.start(); + getThread.interrupt(); + latch1.await(); + exec.shutdownNow(); + exec.awaitTermination(Long.MAX_VALUE, TimeUnit.MILLISECONDS); + assertNotNull("No interrupted exception", iex.get()); + } + + /** + * Tests isStarted() before start() was called. + */ + public void testIsStartedFalse() { + BackgroundInitializerTestImpl init = new BackgroundInitializerTestImpl(); + assertFalse("Already started", init.isStarted()); + } + + /** + * Tests isStarted() after start(). + */ + public void testIsStartedTrue() { + BackgroundInitializerTestImpl init = new BackgroundInitializerTestImpl(); + init.start(); + assertTrue("Not started", init.isStarted()); + } + + /** + * Tests isStarted() after the background task has finished. + */ + public void testIsStartedAfterGet() { + BackgroundInitializerTestImpl init = new BackgroundInitializerTestImpl(); + init.start(); + checkInitialize(init); + assertTrue("Not started", init.isStarted()); + } + + /** + * A concrete implementation of BackgroundInitializer. It also overloads + * some methods that simplify testing. + */ + private static class BackgroundInitializerTestImpl extends + BackgroundInitializer { + /** An exception to be thrown by initialize(). */ + Exception ex; + + /** A flag whether the background task should sleep a while. */ + boolean shouldSleep; + + /** The number of invocations of initialize(). */ + volatile int initializeCalls; + + public BackgroundInitializerTestImpl() { + super(); + } + + public BackgroundInitializerTestImpl(ExecutorService exec) { + super(exec); + } + + /** + * Records this invocation. Optionally throws an exception or sleeps a + * while. + */ + @Override + protected Integer initialize() throws Exception { + if (ex != null) { + throw ex; + } + if (shouldSleep) { + Thread.sleep(60000L); + } + return ++initializeCalls; + } + } +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/concurrent/BasicThreadFactory.java b/ApacheCommonsLang/org/apache/commons/lang3/concurrent/BasicThreadFactory.java new file mode 100644 index 0000000..1da22be --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/concurrent/BasicThreadFactory.java @@ -0,0 +1,383 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.concurrent; + +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.atomic.AtomicLong; + +/** + *

+ * An implementation of the {@code ThreadFactory} interface that provides some + * configuration options for the threads it creates. + *

+ *

+ * A {@code ThreadFactory} is used for instance by an {@code ExecutorService} to + * create the threads it uses for executing tasks. In many cases users do not + * have to care about a {@code ThreadFactory} because the default one used by an + * {@code ExecutorService} will do. However, if there are special requirements + * for the threads, a custom {@code ThreadFactory} has to be created. + *

+ *

+ * This class provides some frequently needed configuration options for the + * threads it creates. These are the following: + *

    + *
  • A name pattern for the threads created by this factory can be specified. + * This is often useful if an application uses multiple executor services for + * different purposes. If the names of the threads used by these services have + * meaningful names, log output or exception traces can be much easier to read. + * Naming patterns are format strings as used by the {@code + * String.format()} method. The string can contain the place holder {@code %d} + * which will be replaced by the number of the current thread ({@code + * ThreadFactoryImpl} keeps a counter of the threads it has already created). + * For instance, the naming pattern {@code "My %d. worker thread"} will result + * in thread names like {@code "My 1. worker thread"}, {@code + * "My 2. worker thread"} and so on.
  • + *
  • A flag whether the threads created by this factory should be daemon + * threads. This can impact the exit behavior of the current Java application + * because the JVM shuts down if there are only daemon threads running.
  • + *
  • The priority of the thread. Here an integer value can be provided. The + * {@code java.lang.Thread} class defines constants for valid ranges of priority + * values.
  • + *
  • The {@code UncaughtExceptionHandler} for the thread. This handler is + * called if an uncaught exception occurs within the thread.
  • + *
+ *

+ *

+ * {@code BasicThreadFactory} wraps another thread factory which actually + * creates new threads. The configuration options are set on the threads created + * by the wrapped thread factory. On construction time the factory to be wrapped + * can be specified. If none is provided, a default {@code ThreadFactory} is + * used. + *

+ *

+ * Instances of {@code BasicThreadFactory} are not created directly, but the + * nested {@code Builder} class is used for this purpose. Using the builder only + * the configuration options an application is interested in need to be set. The + * following example shows how a {@code BasicThreadFactory} is created and + * installed in an {@code ExecutorService}: + * + *

+ * // Create a factory that produces daemon threads with a naming pattern and
+ * // a priority
+ * BasicThreadFactory factory = new BasicThreadFactory.Builder()
+ *     .namingPattern("workerthread-%d")
+ *     .daemon(true)
+ *     .priority(Thread.MAX_PRIORITY)
+ *     .build();
+ * // Create an executor service for single-threaded execution
+ * ExecutorService exec = Executors.newSingleThreadExecutor(factory);
+ * 
+ *

+ * + * @since 3.0 + * @version $Id: BasicThreadFactory.java 1309977 2012-04-05 17:53:39Z ggregory $ + */ +public class BasicThreadFactory implements ThreadFactory { + /** A counter for the threads created by this factory. */ + private final AtomicLong threadCounter; + + /** Stores the wrapped factory. */ + private final ThreadFactory wrappedFactory; + + /** Stores the uncaught exception handler. */ + private final Thread.UncaughtExceptionHandler uncaughtExceptionHandler; + + /** Stores the naming pattern for newly created threads. */ + private final String namingPattern; + + /** Stores the priority. */ + private final Integer priority; + + /** Stores the daemon status flag. */ + private final Boolean daemonFlag; + + /** + * Creates a new instance of {@code ThreadFactoryImpl} and configures it + * from the specified {@code Builder} object. + * + * @param builder the {@code Builder} object + */ + private BasicThreadFactory(Builder builder) { + if (builder.wrappedFactory == null) { + wrappedFactory = Executors.defaultThreadFactory(); + } else { + wrappedFactory = builder.wrappedFactory; + } + + namingPattern = builder.namingPattern; + priority = builder.priority; + daemonFlag = builder.daemonFlag; + uncaughtExceptionHandler = builder.exceptionHandler; + + threadCounter = new AtomicLong(); + } + + /** + * Returns the wrapped {@code ThreadFactory}. This factory is used for + * actually creating threads. This method never returns null. If no + * {@code ThreadFactory} was passed when this object was created, a default + * thread factory is returned. + * + * @return the wrapped {@code ThreadFactory} + */ + public final ThreadFactory getWrappedFactory() { + return wrappedFactory; + } + + /** + * Returns the naming pattern for naming newly created threads. Result can + * be null if no naming pattern was provided. + * + * @return the naming pattern + */ + public final String getNamingPattern() { + return namingPattern; + } + + /** + * Returns the daemon flag. This flag determines whether newly created + * threads should be daemon threads. If true, this factory object + * calls {@code setDaemon(true)} on the newly created threads. Result can be + * null if no daemon flag was provided at creation time. + * + * @return the daemon flag + */ + public final Boolean getDaemonFlag() { + return daemonFlag; + } + + /** + * Returns the priority of the threads created by this factory. Result can + * be null if no priority was specified. + * + * @return the priority for newly created threads + */ + public final Integer getPriority() { + return priority; + } + + /** + * Returns the {@code UncaughtExceptionHandler} for the threads created by + * this factory. Result can be null if no handler was provided. + * + * @return the {@code UncaughtExceptionHandler} + */ + public final Thread.UncaughtExceptionHandler getUncaughtExceptionHandler() { + return uncaughtExceptionHandler; + } + + /** + * Returns the number of threads this factory has already created. This + * class maintains an internal counter that is incremented each time the + * {@link #newThread(Runnable)} method is invoked. + * + * @return the number of threads created by this factory + */ + public long getThreadCount() { + return threadCounter.get(); + } + + /** + * Creates a new thread. This implementation delegates to the wrapped + * factory for creating the thread. Then, on the newly created thread the + * corresponding configuration options are set. + * + * @param r the {@code Runnable} to be executed by the new thread + * @return the newly created thread + */ + @Override + public Thread newThread(Runnable r) { + Thread t = getWrappedFactory().newThread(r); + initializeThread(t); + + return t; + } + + /** + * Initializes the specified thread. This method is called by + * {@link #newThread(Runnable)} after a new thread has been obtained from + * the wrapped thread factory. It initializes the thread according to the + * options set for this factory. + * + * @param t the thread to be initialized + */ + private void initializeThread(Thread t) { + + if (getNamingPattern() != null) { + Long count = Long.valueOf(threadCounter.incrementAndGet()); + t.setName(String.format(getNamingPattern(), count)); + } + + if (getUncaughtExceptionHandler() != null) { + t.setUncaughtExceptionHandler(getUncaughtExceptionHandler()); + } + + if (getPriority() != null) { + t.setPriority(getPriority().intValue()); + } + + if (getDaemonFlag() != null) { + t.setDaemon(getDaemonFlag().booleanValue()); + } + } + + /** + *

+ * A builder class for creating instances of {@code + * BasicThreadFactory}. + *

+ *

+ * Using this builder class instances of {@code BasicThreadFactory} can be + * created and initialized. The class provides methods that correspond to + * the configuration options supported by {@code BasicThreadFactory}. Method + * chaining is supported. Refer to the documentation of {@code + * BasicThreadFactory} for a usage example. + *

+ * + * @version $Id: BasicThreadFactory.java 1309977 2012-04-05 17:53:39Z ggregory $ + */ + public static class Builder + implements org.apache.commons.lang3.builder.Builder { + + /** The wrapped factory. */ + private ThreadFactory wrappedFactory; + + /** The uncaught exception handler. */ + private Thread.UncaughtExceptionHandler exceptionHandler; + + /** The naming pattern. */ + private String namingPattern; + + /** The priority. */ + private Integer priority; + + /** The daemon flag. */ + private Boolean daemonFlag; + + /** + * Sets the {@code ThreadFactory} to be wrapped by the new {@code + * BasicThreadFactory}. + * + * @param factory the wrapped {@code ThreadFactory} (must not be + * null) + * @return a reference to this {@code Builder} + * @throws NullPointerException if the passed in {@code ThreadFactory} + * is null + */ + public Builder wrappedFactory(ThreadFactory factory) { + if (factory == null) { + throw new NullPointerException( + "Wrapped ThreadFactory must not be null!"); + } + + wrappedFactory = factory; + return this; + } + + /** + * Sets the naming pattern to be used by the new {@code + * BasicThreadFactory}. + * + * @param pattern the naming pattern (must not be null) + * @return a reference to this {@code Builder} + * @throws NullPointerException if the naming pattern is null + */ + public Builder namingPattern(String pattern) { + if (pattern == null) { + throw new NullPointerException( + "Naming pattern must not be null!"); + } + + namingPattern = pattern; + return this; + } + + /** + * Sets the daemon flag for the new {@code BasicThreadFactory}. If this + * flag is set to true the new thread factory will create daemon + * threads. + * + * @param f the value of the daemon flag + * @return a reference to this {@code Builder} + */ + public Builder daemon(boolean f) { + daemonFlag = Boolean.valueOf(f); + return this; + } + + /** + * Sets the priority for the threads created by the new {@code + * BasicThreadFactory}. + * + * @param prio the priority + * @return a reference to this {@code Builder} + */ + public Builder priority(int prio) { + priority = Integer.valueOf(prio); + return this; + } + + /** + * Sets the uncaught exception handler for the threads created by the + * new {@code BasicThreadFactory}. + * + * @param handler the {@code UncaughtExceptionHandler} (must not be + * null) + * @return a reference to this {@code Builder} + * @throws NullPointerException if the exception handler is null + */ + public Builder uncaughtExceptionHandler( + Thread.UncaughtExceptionHandler handler) { + if (handler == null) { + throw new NullPointerException( + "Uncaught exception handler must not be null!"); + } + + exceptionHandler = handler; + return this; + } + + /** + * Resets this builder. All configuration options are set to default + * values. Note: If the {@link #build()} method was called, it is not + * necessary to call {@code reset()} explicitly because this is done + * automatically. + */ + public void reset() { + wrappedFactory = null; + exceptionHandler = null; + namingPattern = null; + priority = null; + daemonFlag = null; + } + + /** + * Creates a new {@code BasicThreadFactory} with all configuration + * options that have been specified by calling methods on this builder. + * After creating the factory {@link #reset()} is called. + * + * @return the new {@code BasicThreadFactory} + */ + @Override + public BasicThreadFactory build() { + BasicThreadFactory factory = new BasicThreadFactory(this); + reset(); + return factory; + } + } +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/concurrent/BasicThreadFactoryTest.java b/ApacheCommonsLang/org/apache/commons/lang3/concurrent/BasicThreadFactoryTest.java new file mode 100644 index 0000000..b43155a --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/concurrent/BasicThreadFactoryTest.java @@ -0,0 +1,302 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.concurrent; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; + +import java.util.concurrent.ThreadFactory; + +import org.easymock.EasyMock; +import org.junit.Before; +import org.junit.Test; + +/** + * Test class for {@code BasicThreadFactory}. + * + * @version $Id: BasicThreadFactoryTest.java 1206700 2011-11-27 11:39:35Z sebb $ + */ +public class BasicThreadFactoryTest { + /** Constant for the test naming pattern. */ + private static final String PATTERN = "testThread-%d"; + + /** The builder for creating a thread factory. */ + private BasicThreadFactory.Builder builder; + + @Before + public void setUp() throws Exception { + builder = new BasicThreadFactory.Builder(); + } + + /** + * Tests the default options of a thread factory. + * + * @param factory the factory to be checked + */ + private void checkFactoryDefaults(BasicThreadFactory factory) { + assertNull("Got a naming pattern", factory.getNamingPattern()); + assertNull("Got an exception handler", factory + .getUncaughtExceptionHandler()); + assertNull("Got a priority", factory.getPriority()); + assertNull("Got a daemon flag", factory.getDaemonFlag()); + assertNotNull("No wrapped factory", factory.getWrappedFactory()); + } + + /** + * Tests the default values used by the builder. + */ + @Test + public void testBuildDefaults() { + BasicThreadFactory factory = builder.build(); + checkFactoryDefaults(factory); + } + + /** + * Tries to set a null naming pattern. + */ + @Test(expected = NullPointerException.class) + public void testBuildNamingPatternNull() { + builder.namingPattern(null); + } + + /** + * Tries to set a null wrapped factory. + */ + @Test(expected = NullPointerException.class) + public void testBuildWrappedFactoryNull() { + builder.wrappedFactory(null); + } + + /** + * Tries to set a null exception handler. + */ + @Test(expected = NullPointerException.class) + public void testBuildUncaughtExceptionHandlerNull() { + builder.uncaughtExceptionHandler(null); + } + + /** + * Tests the reset() method of the builder. + */ + @Test + public void testBuilderReset() { + ThreadFactory wrappedFactory = EasyMock.createMock(ThreadFactory.class); + Thread.UncaughtExceptionHandler exHandler = EasyMock + .createMock(Thread.UncaughtExceptionHandler.class); + EasyMock.replay(wrappedFactory, exHandler); + builder.namingPattern(PATTERN).daemon(true).priority( + Thread.MAX_PRIORITY).uncaughtExceptionHandler(exHandler) + .wrappedFactory(wrappedFactory); + builder.reset(); + BasicThreadFactory factory = builder.build(); + checkFactoryDefaults(factory); + assertNotSame("Wrapped factory not reset", wrappedFactory, factory + .getWrappedFactory()); + EasyMock.verify(wrappedFactory, exHandler); + } + + /** + * Tests whether reset() is automatically called after build(). + */ + @Test + public void testBuilderResetAfterBuild() { + builder.wrappedFactory(EasyMock.createNiceMock(ThreadFactory.class)) + .namingPattern(PATTERN).daemon(true).build(); + checkFactoryDefaults(builder.build()); + } + + /** + * Tests whether the naming pattern is applied to new threads. + */ + @Test + public void testNewThreadNamingPattern() { + ThreadFactory wrapped = EasyMock.createMock(ThreadFactory.class); + Runnable r = EasyMock.createMock(Runnable.class); + final int count = 12; + for (int i = 0; i < count; i++) { + EasyMock.expect(wrapped.newThread(r)).andReturn(new Thread()); + } + EasyMock.replay(wrapped, r); + BasicThreadFactory factory = builder.wrappedFactory(wrapped) + .namingPattern(PATTERN).build(); + for (int i = 0; i < count; i++) { + Thread t = factory.newThread(r); + assertEquals("Wrong thread name", String.format(PATTERN, Long + .valueOf(i + 1)), t.getName()); + assertEquals("Wrong thread count", i + 1, factory.getThreadCount()); + } + EasyMock.verify(wrapped, r); + } + + /** + * Tests whether the thread name is not modified if no naming pattern is + * set. + */ + @Test + public void testNewThreadNoNamingPattern() { + ThreadFactory wrapped = EasyMock.createMock(ThreadFactory.class); + Runnable r = EasyMock.createMock(Runnable.class); + final String name = "unchangedThreadName"; + Thread t = new Thread(name); + EasyMock.expect(wrapped.newThread(r)).andReturn(t); + EasyMock.replay(wrapped, r); + BasicThreadFactory factory = builder.wrappedFactory(wrapped).build(); + assertSame("Wrong thread", t, factory.newThread(r)); + assertEquals("Name was changed", name, t.getName()); + EasyMock.verify(wrapped, r); + } + + /** + * Helper method for testing whether the daemon flag is taken into account. + * + * @param flag the value of the flag + */ + private void checkDaemonFlag(boolean flag) { + ThreadFactory wrapped = EasyMock.createMock(ThreadFactory.class); + Runnable r = EasyMock.createMock(Runnable.class); + Thread t = new Thread(); + EasyMock.expect(wrapped.newThread(r)).andReturn(t); + EasyMock.replay(wrapped, r); + BasicThreadFactory factory = builder.wrappedFactory(wrapped).daemon( + flag).build(); + assertSame("Wrong thread", t, factory.newThread(r)); + assertTrue("Wrong daemon flag", flag == t.isDaemon()); + EasyMock.verify(wrapped, r); + } + + /** + * Tests whether daemon threads can be created. + */ + @Test + public void testNewThreadDaemonTrue() { + checkDaemonFlag(true); + } + + /** + * Tests whether the daemon status of new threads can be turned off. + */ + @Test + public void testNewThreadDaemonFalse() { + checkDaemonFlag(false); + } + + /** + * Tests whether the daemon flag is not touched on newly created threads if + * it is not specified. + */ + @Test + public void testNewThreadNoDaemonFlag() { + ThreadFactory wrapped = EasyMock.createMock(ThreadFactory.class); + Runnable r1 = EasyMock.createMock(Runnable.class); + Runnable r2 = EasyMock.createMock(Runnable.class); + Thread t1 = new Thread(); + Thread t2 = new Thread(); + t1.setDaemon(true); + EasyMock.expect(wrapped.newThread(r1)).andReturn(t1); + EasyMock.expect(wrapped.newThread(r2)).andReturn(t2); + EasyMock.replay(wrapped, r1, r2); + BasicThreadFactory factory = builder.wrappedFactory(wrapped).build(); + assertSame("Wrong thread 1", t1, factory.newThread(r1)); + assertTrue("No daemon thread", t1.isDaemon()); + assertSame("Wrong thread 2", t2, factory.newThread(r2)); + assertFalse("A daemon thread", t2.isDaemon()); + EasyMock.verify(wrapped, r1, r2); + } + + /** + * Tests whether the priority is set on newly created threads. + */ + @Test + public void testNewThreadPriority() { + ThreadFactory wrapped = EasyMock.createMock(ThreadFactory.class); + Runnable r = EasyMock.createMock(Runnable.class); + Thread t = new Thread(); + EasyMock.expect(wrapped.newThread(r)).andReturn(t); + EasyMock.replay(wrapped, r); + final int priority = Thread.NORM_PRIORITY + 1; + BasicThreadFactory factory = builder.wrappedFactory(wrapped).priority( + priority).build(); + assertSame("Wrong thread", t, factory.newThread(r)); + assertEquals("Wrong priority", priority, t.getPriority()); + EasyMock.verify(wrapped, r); + } + + /** + * Tests whether the original priority is not changed if no priority is + * specified. + */ + @Test + public void testNewThreadNoPriority() { + ThreadFactory wrapped = EasyMock.createMock(ThreadFactory.class); + Runnable r = EasyMock.createMock(Runnable.class); + final int orgPriority = Thread.NORM_PRIORITY + 1; + Thread t = new Thread(); + t.setPriority(orgPriority); + EasyMock.expect(wrapped.newThread(r)).andReturn(t); + EasyMock.replay(wrapped, r); + BasicThreadFactory factory = builder.wrappedFactory(wrapped).build(); + assertSame("Wrong thread", t, factory.newThread(r)); + assertEquals("Wrong priority", orgPriority, t.getPriority()); + EasyMock.verify(wrapped, r); + } + + /** + * Tests whether the exception handler is set if one is provided. + */ + @Test + public void testNewThreadExHandler() { + ThreadFactory wrapped = EasyMock.createMock(ThreadFactory.class); + Runnable r = EasyMock.createMock(Runnable.class); + Thread.UncaughtExceptionHandler handler = EasyMock + .createMock(Thread.UncaughtExceptionHandler.class); + Thread t = new Thread(); + EasyMock.expect(wrapped.newThread(r)).andReturn(t); + EasyMock.replay(wrapped, r, handler); + BasicThreadFactory factory = builder.wrappedFactory(wrapped) + .uncaughtExceptionHandler(handler).build(); + assertSame("Wrong thread", t, factory.newThread(r)); + assertEquals("Wrong exception handler", handler, t + .getUncaughtExceptionHandler()); + EasyMock.verify(wrapped, r, handler); + } + + /** + * Tests whether the original exception hander is not touched if none is + * specified. + */ + @Test + public void testNewThreadNoExHandler() { + ThreadFactory wrapped = EasyMock.createMock(ThreadFactory.class); + Runnable r = EasyMock.createMock(Runnable.class); + Thread.UncaughtExceptionHandler handler = EasyMock + .createMock(Thread.UncaughtExceptionHandler.class); + Thread t = new Thread(); + t.setUncaughtExceptionHandler(handler); + EasyMock.expect(wrapped.newThread(r)).andReturn(t); + EasyMock.replay(wrapped, r, handler); + BasicThreadFactory factory = builder.wrappedFactory(wrapped).build(); + assertSame("Wrong thread", t, factory.newThread(r)); + assertEquals("Wrong exception handler", handler, t + .getUncaughtExceptionHandler()); + EasyMock.verify(wrapped, r, handler); + } +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/concurrent/CallableBackgroundInitializer.java b/ApacheCommonsLang/org/apache/commons/lang3/concurrent/CallableBackgroundInitializer.java new file mode 100644 index 0000000..f3522bd --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/concurrent/CallableBackgroundInitializer.java @@ -0,0 +1,126 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.concurrent; + +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; + +/** + *

+ * A specialized {@link BackgroundInitializer} implementation that wraps a + * {@code Callable} object. + *

+ *

+ * An instance of this class is initialized with a {@code Callable} object when + * it is constructed. The implementation of the {@link #initialize()} method + * defined in the super class delegates to this {@code Callable} so that the + * {@code Callable} is executed in the background thread. + *

+ *

+ * The {@code java.util.concurrent.Callable} interface is a standard mechanism + * of the JDK to define tasks to be executed by another thread. The {@code + * CallableBackgroundInitializer} class allows combining this standard interface + * with the background initializer API. + *

+ *

+ * Usage of this class is very similar to the default usage pattern of the + * {@link BackgroundInitializer} class: Just create an instance and provide the + * {@code Callable} object to be executed, then call the initializer's + * {@link #start()} method. This causes the {@code Callable} to be executed in + * another thread. When the results of the {@code Callable} are needed the + * initializer's {@link #get()} method can be called (which may block until + * background execution is complete). The following code fragment shows a + * typical usage example: + * + *

+ * // a Callable that performs a complex computation
+ * Callable<Integer> computationCallable = new MyComputationCallable();
+ * // setup the background initializer
+ * CallableBackgroundInitializer<Integer> initializer =
+ *     new CallableBackgroundInitializer(computationCallable);
+ * initializer.start();
+ * // Now do some other things. Initialization runs in a parallel thread
+ * ...
+ * // Wait for the end of initialization and access the result
+ * Integer result = initializer.get();
+ * 
+ * + *

+ * + * @since 3.0 + * @version $Id: CallableBackgroundInitializer.java 1082044 2011-03-16 04:26:58Z bayard $ + * @param the type of the object managed by this initializer class + */ +public class CallableBackgroundInitializer extends BackgroundInitializer { + /** The Callable to be executed. */ + private final Callable callable; + + /** + * Creates a new instance of {@code CallableBackgroundInitializer} and sets + * the {@code Callable} to be executed in a background thread. + * + * @param call the {@code Callable} (must not be null) + * @throws IllegalArgumentException if the {@code Callable} is null + */ + public CallableBackgroundInitializer(Callable call) { + checkCallable(call); + callable = call; + } + + /** + * Creates a new instance of {@code CallableBackgroundInitializer} and + * initializes it with the {@code Callable} to be executed in a background + * thread and the {@code ExecutorService} for managing the background + * execution. + * + * @param call the {@code Callable} (must not be null) + * @param exec an external {@code ExecutorService} to be used for task + * execution + * @throws IllegalArgumentException if the {@code Callable} is null + */ + public CallableBackgroundInitializer(Callable call, ExecutorService exec) { + super(exec); + checkCallable(call); + callable = call; + } + + /** + * Performs initialization in a background thread. This implementation + * delegates to the {@code Callable} passed at construction time of this + * object. + * + * @return the result of the initialization + * @throws Exception if an error occurs + */ + @Override + protected T initialize() throws Exception { + return callable.call(); + } + + /** + * Tests the passed in {@code Callable} and throws an exception if it is + * undefined. + * + * @param call the object to check + * @throws IllegalArgumentException if the {@code Callable} is null + */ + private void checkCallable(Callable call) { + if (call == null) { + throw new IllegalArgumentException("Callable must not be null!"); + } + } +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/concurrent/CallableBackgroundInitializerTest.java b/ApacheCommonsLang/org/apache/commons/lang3/concurrent/CallableBackgroundInitializerTest.java new file mode 100644 index 0000000..ac5432c --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/concurrent/CallableBackgroundInitializerTest.java @@ -0,0 +1,100 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.concurrent; + +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import junit.framework.TestCase; + +/** + * Test class for {@code CallableBackgroundInitializer} + * + * @version $Id: CallableBackgroundInitializerTest.java 1309977 2012-04-05 17:53:39Z ggregory $ + */ +public class CallableBackgroundInitializerTest extends TestCase { + /** Constant for the result of the call() invocation. */ + private static final Integer RESULT = Integer.valueOf(42); + + /** + * Tries to create an instance without a Callable. This should cause an + * exception. + */ + public void testInitNullCallable() { + try { + new CallableBackgroundInitializer(null); + fail("Could create instance without a Callable!"); + } catch (IllegalArgumentException iex) { + // ok + } + } + + /** + * Tests whether the executor service is correctly passed to the super + * class. + */ + public void testInitExecutor() { + ExecutorService exec = Executors.newSingleThreadExecutor(); + CallableBackgroundInitializer init = new CallableBackgroundInitializer( + new TestCallable(), exec); + assertEquals("Executor not set", exec, init.getExternalExecutor()); + } + + /** + * Tries to pass a null Callable to the constructor that takes an executor. + * This should cause an exception. + */ + public void testInitExecutorNullCallable() { + ExecutorService exec = Executors.newSingleThreadExecutor(); + try { + new CallableBackgroundInitializer(null, exec); + fail("Could create instance without a Callable!"); + } catch (IllegalArgumentException iex) { + // ok + } + } + + /** + * Tests the implementation of initialize(). + */ + public void testInitialize() throws Exception { + TestCallable call = new TestCallable(); + CallableBackgroundInitializer init = new CallableBackgroundInitializer( + call); + assertEquals("Wrong result", RESULT, init.initialize()); + assertEquals("Wrong number of invocations", 1, call.callCount); + } + + /** + * A test Callable implementation for checking the initializer's + * implementation of the initialize() method. + */ + private static class TestCallable implements Callable { + /** A counter for the number of call() invocations. */ + int callCount; + + /** + * Records this invocation and returns the test result. + */ + @Override + public Integer call() throws Exception { + callCount++; + return RESULT; + } + } +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/concurrent/ConcurrentException.java b/ApacheCommonsLang/org/apache/commons/lang3/concurrent/ConcurrentException.java new file mode 100644 index 0000000..31e3f3c --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/concurrent/ConcurrentException.java @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.concurrent; + +/** + *

+ * An exception class used for reporting error conditions related to accessing + * data of background tasks. + *

+ *

+ * The purpose of this exception class is analogous to the default JDK exception + * class {@link java.util.concurrent.ExecutionException}, i.e. it wraps an + * exception that occurred during the execution of a task. However, in contrast + * to {@code ExecutionException}, it wraps only checked exceptions. Runtime + * exceptions are thrown directly. + *

+ * + * @since 3.0 + * @version $Id: ConcurrentException.java 1088899 2011-04-05 05:31:27Z bayard $ + */ +public class ConcurrentException extends Exception { + /** + * The serial version UID. + */ + private static final long serialVersionUID = 6622707671812226130L; + + /** + * Creates a new, uninitialized instance of {@code ConcurrentException}. + */ + protected ConcurrentException() { + super(); + } + + /** + * Creates a new instance of {@code ConcurrentException} and initializes it + * with the given cause. + * + * @param cause the cause of this exception + * @throws IllegalArgumentException if the cause is not a checked exception + */ + public ConcurrentException(Throwable cause) { + super(ConcurrentUtils.checkedException(cause)); + } + + /** + * Creates a new instance of {@code ConcurrentException} and initializes it + * with the given message and cause. + * + * @param msg the error message + * @param cause the cause of this exception + * @throws IllegalArgumentException if the cause is not a checked exception + */ + public ConcurrentException(String msg, Throwable cause) { + super(msg, ConcurrentUtils.checkedException(cause)); + } +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/concurrent/ConcurrentInitializer.java b/ApacheCommonsLang/org/apache/commons/lang3/concurrent/ConcurrentInitializer.java new file mode 100644 index 0000000..3ed4faf --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/concurrent/ConcurrentInitializer.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.concurrent; + +/** + *

+ * Definition of an interface for the thread-safe initialization of objects. + *

+ *

+ * The idea behind this interface is to provide access to an object in a + * thread-safe manner. A {@code ConcurrentInitializer} can be passed to multiple + * threads which can all access the object produced by the initializer. Through + * the {@link #get()} method the object can be queried. + *

+ *

+ * Concrete implementations of this interface will use different strategies for + * the creation of the managed object, e.g. lazy initialization or + * initialization in a background thread. This is completely transparent to + * client code, so it is possible to change the initialization strategy without + * affecting clients. + *

+ * + * @since 3.0 + * @version $Id: ConcurrentInitializer.java 1088899 2011-04-05 05:31:27Z bayard $ + * @param the type of the object managed by this initializer class + */ +public interface ConcurrentInitializer { + /** + * Returns the fully initialized object produced by this {@code + * ConcurrentInitializer}. A concrete implementation here returns the + * results of the initialization process. This method may block until + * results are available. Typically, once created the result object is + * always the same. + * + * @return the object created by this {@code ConcurrentException} + * @throws ConcurrentException if an error occurred during initialization of + * the object + */ + T get() throws ConcurrentException; +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/concurrent/ConcurrentRuntimeException.java b/ApacheCommonsLang/org/apache/commons/lang3/concurrent/ConcurrentRuntimeException.java new file mode 100644 index 0000000..6ec1d01 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/concurrent/ConcurrentRuntimeException.java @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.concurrent; + +/** + *

+ * An exception class used for reporting runtime error conditions related to + * accessing data of background tasks. + *

+ *

+ * This class is an analogon of the {@link ConcurrentException} exception class. + * However, it is a runtime exception and thus does not need explicit catch + * clauses. Some methods of {@link ConcurrentUtils} throw {@code + * ConcurrentRuntimeException} exceptions rather than + * {@link ConcurrentException} exceptions. They can be used by client code that + * does not want to be bothered with checked exceptions. + *

+ * + * @since 3.0 + * @version $Id: ConcurrentRuntimeException.java 1088899 2011-04-05 05:31:27Z bayard $ + */ +public class ConcurrentRuntimeException extends RuntimeException { + /** + * The serial version UID. + */ + private static final long serialVersionUID = -6582182735562919670L; + + /** + * Creates a new, uninitialized instance of {@code + * ConcurrentRuntimeException}. + */ + protected ConcurrentRuntimeException() { + super(); + } + + /** + * Creates a new instance of {@code ConcurrentRuntimeException} and + * initializes it with the given cause. + * + * @param cause the cause of this exception + * @throws IllegalArgumentException if the cause is not a checked exception + */ + public ConcurrentRuntimeException(Throwable cause) { + super(ConcurrentUtils.checkedException(cause)); + } + + /** + * Creates a new instance of {@code ConcurrentRuntimeException} and + * initializes it with the given message and cause. + * + * @param msg the error message + * @param cause the cause of this exception + * @throws IllegalArgumentException if the cause is not a checked exception + */ + public ConcurrentRuntimeException(String msg, Throwable cause) { + super(msg, ConcurrentUtils.checkedException(cause)); + } +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/concurrent/ConcurrentUtils.java b/ApacheCommonsLang/org/apache/commons/lang3/concurrent/ConcurrentUtils.java new file mode 100644 index 0000000..740111c --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/concurrent/ConcurrentUtils.java @@ -0,0 +1,391 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.concurrent; + +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +/** + *

+ * An utility class providing functionality related to the {@code + * java.util.concurrent} package. + *

+ * + * @since 3.0 + * @version $Id: ConcurrentUtils.java 1309977 2012-04-05 17:53:39Z ggregory $ + */ +public class ConcurrentUtils { + + /** + * Private constructor so that no instances can be created. This class + * contains only static utility methods. + */ + private ConcurrentUtils() { + } + + /** + * Inspects the cause of the specified {@code ExecutionException} and + * creates a {@code ConcurrentException} with the checked cause if + * necessary. This method performs the following checks on the cause of the + * passed in exception: + *
    + *
  • If the passed in exception is null or the cause is + * null, this method returns null.
  • + *
  • If the cause is a runtime exception, it is directly thrown.
  • + *
  • If the cause is an error, it is directly thrown, too.
  • + *
  • In any other case the cause is a checked exception. The method then + * creates a {@link ConcurrentException}, initializes it with the cause, and + * returns it.
  • + *
+ * + * @param ex the exception to be processed + * @return a {@code ConcurrentException} with the checked cause + */ + public static ConcurrentException extractCause(ExecutionException ex) { + if (ex == null || ex.getCause() == null) { + return null; + } + + throwCause(ex); + return new ConcurrentException(ex.getMessage(), ex.getCause()); + } + + /** + * Inspects the cause of the specified {@code ExecutionException} and + * creates a {@code ConcurrentRuntimeException} with the checked cause if + * necessary. This method works exactly like + * {@link #extractCause(ExecutionException)}. The only difference is that + * the cause of the specified {@code ExecutionException} is extracted as a + * runtime exception. This is an alternative for client code that does not + * want to deal with checked exceptions. + * + * @param ex the exception to be processed + * @return a {@code ConcurrentRuntimeException} with the checked cause + */ + public static ConcurrentRuntimeException extractCauseUnchecked( + ExecutionException ex) { + if (ex == null || ex.getCause() == null) { + return null; + } + + throwCause(ex); + return new ConcurrentRuntimeException(ex.getMessage(), ex.getCause()); + } + + /** + * Handles the specified {@code ExecutionException}. This method calls + * {@link #extractCause(ExecutionException)} for obtaining the cause of the + * exception - which might already cause an unchecked exception or an error + * being thrown. If the cause is a checked exception however, it is wrapped + * in a {@code ConcurrentException}, which is thrown. If the passed in + * exception is null or has no cause, the method simply returns + * without throwing an exception. + * + * @param ex the exception to be handled + * @throws ConcurrentException if the cause of the {@code + * ExecutionException} is a checked exception + */ + public static void handleCause(ExecutionException ex) + throws ConcurrentException { + ConcurrentException cex = extractCause(ex); + + if (cex != null) { + throw cex; + } + } + + /** + * Handles the specified {@code ExecutionException} and transforms it into a + * runtime exception. This method works exactly like + * {@link #handleCause(ExecutionException)}, but instead of a + * {@link ConcurrentException} it throws a + * {@link ConcurrentRuntimeException}. This is an alternative for client + * code that does not want to deal with checked exceptions. + * + * @param ex the exception to be handled + * @throws ConcurrentRuntimeException if the cause of the {@code + * ExecutionException} is a checked exception; this exception is then + * wrapped in the thrown runtime exception + */ + public static void handleCauseUnchecked(ExecutionException ex) { + ConcurrentRuntimeException crex = extractCauseUnchecked(ex); + + if (crex != null) { + throw crex; + } + } + + /** + * Tests whether the specified {@code Throwable} is a checked exception. If + * not, an exception is thrown. + * + * @param ex the {@code Throwable} to check + * @return a flag whether the passed in exception is a checked exception + * @throws IllegalArgumentException if the {@code Throwable} is not a + * checked exception + */ + static Throwable checkedException(Throwable ex) { + if (ex != null && !(ex instanceof RuntimeException) + && !(ex instanceof Error)) { + return ex; + } else { + throw new IllegalArgumentException("Not a checked exception: " + ex); + } + } + + /** + * Tests whether the cause of the specified {@code ExecutionException} + * should be thrown and does it if necessary. + * + * @param ex the exception in question + */ + private static void throwCause(ExecutionException ex) { + if (ex.getCause() instanceof RuntimeException) { + throw (RuntimeException) ex.getCause(); + } + + if (ex.getCause() instanceof Error) { + throw (Error) ex.getCause(); + } + } + + //----------------------------------------------------------------------- + /** + * Invokes the specified {@code ConcurrentInitializer} and returns the + * object produced by the initializer. This method just invokes the {@code + * get()} method of the given {@code ConcurrentInitializer}. It is + * null-safe: if the argument is null, result is also + * null. + * + * @param the type of the object produced by the initializer + * @param initializer the {@code ConcurrentInitializer} to be invoked + * @return the object managed by the {@code ConcurrentInitializer} + * @throws ConcurrentException if the {@code ConcurrentInitializer} throws + * an exception + */ + public static T initialize(ConcurrentInitializer initializer) + throws ConcurrentException { + return initializer != null ? initializer.get() : null; + } + + /** + * Invokes the specified {@code ConcurrentInitializer} and transforms + * occurring exceptions to runtime exceptions. This method works like + * {@link #initialize(ConcurrentInitializer)}, but if the {@code + * ConcurrentInitializer} throws a {@link ConcurrentException}, it is + * caught, and the cause is wrapped in a {@link ConcurrentRuntimeException}. + * So client code does not have to deal with checked exceptions. + * + * @param the type of the object produced by the initializer + * @param initializer the {@code ConcurrentInitializer} to be invoked + * @return the object managed by the {@code ConcurrentInitializer} + * @throws ConcurrentRuntimeException if the initializer throws an exception + */ + public static T initializeUnchecked(ConcurrentInitializer initializer) { + try { + return initialize(initializer); + } catch (ConcurrentException cex) { + throw new ConcurrentRuntimeException(cex.getCause()); + } + } + + //----------------------------------------------------------------------- + /** + *

+ * Puts a value in the specified {@code ConcurrentMap} if the key is not yet + * present. This method works similar to the {@code putIfAbsent()} method of + * the {@code ConcurrentMap} interface, but the value returned is different. + * Basically, this method is equivalent to the following code fragment: + * + *

+     * if (!map.containsKey(key)) {
+     *     map.put(key, value);
+     *     return value;
+     * } else {
+     *     return map.get(key);
+     * }
+     * 
+ * + * except that the action is performed atomically. So this method always + * returns the value which is stored in the map. + *

+ *

+ * This method is null-safe: It accepts a null map as input + * without throwing an exception. In this case the return value is + * null, too. + *

+ * + * @param the type of the keys of the map + * @param the type of the values of the map + * @param map the map to be modified + * @param key the key of the value to be added + * @param value the value to be added + * @return the value stored in the map after this operation + */ + public static V putIfAbsent(ConcurrentMap map, K key, V value) { + if (map == null) { + return null; + } + + V result = map.putIfAbsent(key, value); + return result != null ? result : value; + } + + /** + * Checks if a concurrent map contains a key and creates a corresponding + * value if not. This method first checks the presence of the key in the + * given map. If it is already contained, its value is returned. Otherwise + * the {@code get()} method of the passed in {@link ConcurrentInitializer} + * is called. With the resulting object + * {@link #putIfAbsent(ConcurrentMap, Object, Object)} is called. This + * handles the case that in the meantime another thread has added the key to + * the map. Both the map and the initializer can be null; in this + * case this method simply returns null. + * + * @param the type of the keys of the map + * @param the type of the values of the map + * @param map the map to be modified + * @param key the key of the value to be added + * @param init the {@link ConcurrentInitializer} for creating the value + * @return the value stored in the map after this operation; this may or may + * not be the object created by the {@link ConcurrentInitializer} + * @throws ConcurrentException if the initializer throws an exception + */ + public static V createIfAbsent(ConcurrentMap map, K key, + ConcurrentInitializer init) throws ConcurrentException { + if (map == null || init == null) { + return null; + } + + V value = map.get(key); + if (value == null) { + return putIfAbsent(map, key, init.get()); + } + return value; + } + + /** + * Checks if a concurrent map contains a key and creates a corresponding + * value if not, suppressing checked exceptions. This method calls + * {@code createIfAbsent()}. If a {@link ConcurrentException} is thrown, it + * is caught and re-thrown as a {@link ConcurrentRuntimeException}. + * + * @param the type of the keys of the map + * @param the type of the values of the map + * @param map the map to be modified + * @param key the key of the value to be added + * @param init the {@link ConcurrentInitializer} for creating the value + * @return the value stored in the map after this operation; this may or may + * not be the object created by the {@link ConcurrentInitializer} + * @throws ConcurrentRuntimeException if the initializer throws an exception + */ + public static V createIfAbsentUnchecked(ConcurrentMap map, + K key, ConcurrentInitializer init) { + try { + return createIfAbsent(map, key, init); + } catch (ConcurrentException cex) { + throw new ConcurrentRuntimeException(cex.getCause()); + } + } + + //----------------------------------------------------------------------- + /** + *

+ * Gets an implementation of Future that is immediately done + * and returns the specified constant value. + *

+ *

+ * This can be useful to return a simple constant immediately from the + * concurrent processing, perhaps as part of avoiding nulls. + * A constant future can also be useful in testing. + *

+ * + * @param the type of the value used by this {@code Future} object + * @param value the constant value to return, may be null + * @return an instance of Future that will return the value, never null + */ + public static Future constantFuture(T value) { + return new ConstantFuture(value); + } + + /** + * A specialized {@code Future} implementation which wraps a constant value. + * @param the type of the value wrapped by this class + */ + static final class ConstantFuture implements Future { + /** The constant value. */ + private final T value; + + /** + * Creates a new instance of {@code ConstantFuture} and initializes it + * with the constant value. + * + * @param value the value (may be null) + */ + ConstantFuture(T value) { + this.value = value; + } + + /** + * {@inheritDoc} This implementation always returns true because + * the constant object managed by this {@code Future} implementation is + * always available. + */ + @Override + public boolean isDone() { + return true; + } + + /** + * {@inheritDoc} This implementation just returns the constant value. + */ + @Override + public T get() { + return value; + } + + /** + * {@inheritDoc} This implementation just returns the constant value; it + * does not block, therefore the timeout has no meaning. + */ + @Override + public T get(long timeout, TimeUnit unit) { + return value; + } + + /** + * {@inheritDoc} This implementation always returns false; there + * is no background process which could be cancelled. + */ + @Override + public boolean isCancelled() { + return false; + } + + /** + * {@inheritDoc} The cancel operation is not supported. This + * implementation always returns false. + */ + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + return false; + } + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/concurrent/ConcurrentUtilsTest.java b/ApacheCommonsLang/org/apache/commons/lang3/concurrent/ConcurrentUtilsTest.java new file mode 100644 index 0000000..63b9310 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/concurrent/ConcurrentUtilsTest.java @@ -0,0 +1,548 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.concurrent; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +import org.easymock.EasyMock; +import org.junit.Test; + +/** + * Test class for {@link ConcurrentUtils}. + * + * @version $Id: ConcurrentUtilsTest.java 1153484 2011-08-03 13:39:42Z ggregory $ + */ +public class ConcurrentUtilsTest { + /** + * Tests creating a ConcurrentException with a runtime exception as cause. + */ + @Test(expected = IllegalArgumentException.class) + public void testConcurrentExceptionCauseUnchecked() { + new ConcurrentException(new RuntimeException()); + } + + /** + * Tests creating a ConcurrentException with an error as cause. + */ + @Test(expected = IllegalArgumentException.class) + public void testConcurrentExceptionCauseError() { + new ConcurrentException("An error", new Error()); + } + + /** + * Tests creating a ConcurrentException with null as cause. + */ + @Test(expected = IllegalArgumentException.class) + public void testConcurrentExceptionCauseNull() { + new ConcurrentException(null); + } + + /** + * Tries to create a ConcurrentRuntimeException with a runtime as cause. + */ + @Test(expected = IllegalArgumentException.class) + public void testConcurrentRuntimeExceptionCauseUnchecked() { + new ConcurrentRuntimeException(new RuntimeException()); + } + + /** + * Tries to create a ConcurrentRuntimeException with an error as cause. + */ + @Test(expected = IllegalArgumentException.class) + public void testConcurrentRuntimeExceptionCauseError() { + new ConcurrentRuntimeException("An error", new Error()); + } + + /** + * Tries to create a ConcurrentRuntimeException with null as cause. + */ + @Test(expected = IllegalArgumentException.class) + public void testConcurrentRuntimeExceptionCauseNull() { + new ConcurrentRuntimeException(null); + } + + /** + * Tests extractCause() for a null exception. + */ + @Test + public void testExtractCauseNull() { + assertNull("Non null result", ConcurrentUtils.extractCause(null)); + } + + /** + * Tests extractCause() if the cause of the passed in exception is null. + */ + @Test + public void testExtractCauseNullCause() { + assertNull("Non null result", ConcurrentUtils + .extractCause(new ExecutionException("Test", null))); + } + + /** + * Tests extractCause() if the cause is an error. + */ + @Test + public void testExtractCauseError() { + Error err = new AssertionError("Test"); + try { + ConcurrentUtils.extractCause(new ExecutionException(err)); + fail("Error not thrown!"); + } catch (Error e) { + assertEquals("Wrong error", err, e); + } + } + + /** + * Tests extractCause() if the cause is an unchecked exception. + */ + @Test + public void testExtractCauseUncheckedException() { + RuntimeException rex = new RuntimeException("Test"); + try { + ConcurrentUtils.extractCause(new ExecutionException(rex)); + fail("Runtime exception not thrown!"); + } catch (RuntimeException r) { + assertEquals("Wrong exception", rex, r); + } + } + + /** + * Tests extractCause() if the cause is a checked exception. + */ + @Test + public void testExtractCauseChecked() { + Exception ex = new Exception("Test"); + ConcurrentException cex = ConcurrentUtils + .extractCause(new ExecutionException(ex)); + assertSame("Wrong cause", ex, cex.getCause()); + } + + /** + * Tests extractCauseUnchecked() for a null exception. + */ + @Test + public void testExtractCauseUncheckedNull() { + assertNull("Non null result", ConcurrentUtils.extractCauseUnchecked(null)); + } + + /** + * Tests extractCauseUnchecked() if the cause of the passed in exception is null. + */ + @Test + public void testExtractCauseUncheckedNullCause() { + assertNull("Non null result", ConcurrentUtils + .extractCauseUnchecked(new ExecutionException("Test", null))); + } + + /** + * Tests extractCauseUnchecked() if the cause is an error. + */ + @Test + public void testExtractCauseUncheckedError() { + Error err = new AssertionError("Test"); + try { + ConcurrentUtils.extractCauseUnchecked(new ExecutionException(err)); + fail("Error not thrown!"); + } catch (Error e) { + assertEquals("Wrong error", err, e); + } + } + + /** + * Tests extractCauseUnchecked() if the cause is an unchecked exception. + */ + @Test + public void testExtractCauseUncheckedUncheckedException() { + RuntimeException rex = new RuntimeException("Test"); + try { + ConcurrentUtils.extractCauseUnchecked(new ExecutionException(rex)); + fail("Runtime exception not thrown!"); + } catch (RuntimeException r) { + assertEquals("Wrong exception", rex, r); + } + } + + /** + * Tests extractCauseUnchecked() if the cause is a checked exception. + */ + @Test + public void testExtractCauseUncheckedChecked() { + Exception ex = new Exception("Test"); + ConcurrentRuntimeException cex = ConcurrentUtils + .extractCauseUnchecked(new ExecutionException(ex)); + assertSame("Wrong cause", ex, cex.getCause()); + } + + /** + * Tests handleCause() if the cause is an error. + */ + @Test + public void testHandleCauseError() throws ConcurrentException { + Error err = new AssertionError("Test"); + try { + ConcurrentUtils.handleCause(new ExecutionException(err)); + fail("Error not thrown!"); + } catch (Error e) { + assertEquals("Wrong error", err, e); + } + } + + /** + * Tests handleCause() if the cause is an unchecked exception. + */ + @Test + public void testHandleCauseUncheckedException() throws ConcurrentException { + RuntimeException rex = new RuntimeException("Test"); + try { + ConcurrentUtils.handleCause(new ExecutionException(rex)); + fail("Runtime exception not thrown!"); + } catch (RuntimeException r) { + assertEquals("Wrong exception", rex, r); + } + } + + /** + * Tests handleCause() if the cause is a checked exception. + */ + @Test + public void testHandleCauseChecked() { + Exception ex = new Exception("Test"); + try { + ConcurrentUtils.handleCause(new ExecutionException(ex)); + fail("ConcurrentException not thrown!"); + } catch (ConcurrentException cex) { + assertEquals("Wrong cause", ex, cex.getCause()); + } + } + + /** + * Tests handleCause() for a null parameter or a null cause. In this case + * the method should do nothing. We can only test that no exception is + * thrown. + */ + @Test + public void testHandleCauseNull() throws ConcurrentException { + ConcurrentUtils.handleCause(null); + ConcurrentUtils.handleCause(new ExecutionException("Test", null)); + } + + /** + * Tests handleCauseUnchecked() if the cause is an error. + */ + @Test + public void testHandleCauseUncheckedError() { + Error err = new AssertionError("Test"); + try { + ConcurrentUtils.handleCauseUnchecked(new ExecutionException(err)); + fail("Error not thrown!"); + } catch (Error e) { + assertEquals("Wrong error", err, e); + } + } + + /** + * Tests handleCauseUnchecked() if the cause is an unchecked exception. + */ + @Test + public void testHandleCauseUncheckedUncheckedException() { + RuntimeException rex = new RuntimeException("Test"); + try { + ConcurrentUtils.handleCauseUnchecked(new ExecutionException(rex)); + fail("Runtime exception not thrown!"); + } catch (RuntimeException r) { + assertEquals("Wrong exception", rex, r); + } + } + + /** + * Tests handleCauseUnchecked() if the cause is a checked exception. + */ + @Test + public void testHandleCauseUncheckedChecked() { + Exception ex = new Exception("Test"); + try { + ConcurrentUtils.handleCauseUnchecked(new ExecutionException(ex)); + fail("ConcurrentRuntimeException not thrown!"); + } catch (ConcurrentRuntimeException crex) { + assertEquals("Wrong cause", ex, crex.getCause()); + } + } + + /** + * Tests handleCauseUnchecked() for a null parameter or a null cause. In + * this case the method should do nothing. We can only test that no + * exception is thrown. + */ + @Test + public void testHandleCauseUncheckedNull() { + ConcurrentUtils.handleCauseUnchecked(null); + ConcurrentUtils.handleCauseUnchecked(new ExecutionException("Test", + null)); + } + + //----------------------------------------------------------------------- + /** + * Tests initialize() for a null argument. + */ + @Test + public void testInitializeNull() throws ConcurrentException { + assertNull("Got a result", ConcurrentUtils.initialize(null)); + } + + /** + * Tests a successful initialize() operation. + */ + @Test + public void testInitialize() throws ConcurrentException { + @SuppressWarnings("unchecked") + ConcurrentInitializer init = EasyMock + .createMock(ConcurrentInitializer.class); + final Object result = new Object(); + EasyMock.expect(init.get()).andReturn(result); + EasyMock.replay(init); + assertSame("Wrong result object", result, ConcurrentUtils + .initialize(init)); + EasyMock.verify(init); + } + + /** + * Tests initializeUnchecked() for a null argument. + */ + @Test + public void testInitializeUncheckedNull() { + assertNull("Got a result", ConcurrentUtils.initializeUnchecked(null)); + } + + /** + * Tests a successful initializeUnchecked() operation. + */ + @Test + public void testInitializeUnchecked() throws ConcurrentException { + @SuppressWarnings("unchecked") + ConcurrentInitializer init = EasyMock + .createMock(ConcurrentInitializer.class); + final Object result = new Object(); + EasyMock.expect(init.get()).andReturn(result); + EasyMock.replay(init); + assertSame("Wrong result object", result, ConcurrentUtils + .initializeUnchecked(init)); + EasyMock.verify(init); + } + + /** + * Tests whether exceptions are correctly handled by initializeUnchecked(). + */ + @Test + public void testInitializeUncheckedEx() throws ConcurrentException { + @SuppressWarnings("unchecked") + ConcurrentInitializer init = EasyMock + .createMock(ConcurrentInitializer.class); + final Exception cause = new Exception(); + EasyMock.expect(init.get()).andThrow(new ConcurrentException(cause)); + EasyMock.replay(init); + try { + ConcurrentUtils.initializeUnchecked(init); + fail("Exception not thrown!"); + } catch (ConcurrentRuntimeException crex) { + assertSame("Wrong cause", cause, crex.getCause()); + } + EasyMock.verify(init); + } + + //----------------------------------------------------------------------- + /** + * Tests constant future. + */ + @Test + public void testConstantFuture_Integer() throws Exception { + Integer value = Integer.valueOf(5); + Future test = ConcurrentUtils.constantFuture(value); + assertTrue(test.isDone()); + assertSame(value, test.get()); + assertSame(value, test.get(1000, TimeUnit.SECONDS)); + assertSame(value, test.get(1000, null)); + assertFalse(test.isCancelled()); + assertFalse(test.cancel(true)); + assertFalse(test.cancel(false)); + } + + /** + * Tests constant future. + */ + @Test + public void testConstantFuture_null() throws Exception { + Integer value = null; + Future test = ConcurrentUtils.constantFuture(value); + assertTrue(test.isDone()); + assertSame(value, test.get()); + assertSame(value, test.get(1000, TimeUnit.SECONDS)); + assertSame(value, test.get(1000, null)); + assertFalse(test.isCancelled()); + assertFalse(test.cancel(true)); + assertFalse(test.cancel(false)); + } + + //----------------------------------------------------------------------- + /** + * Tests putIfAbsent() if the map contains the key in question. + */ + @Test + public void testPutIfAbsentKeyPresent() { + final String key = "testKey"; + final Integer value = 42; + ConcurrentMap map = new ConcurrentHashMap(); + map.put(key, value); + assertEquals("Wrong result", value, + ConcurrentUtils.putIfAbsent(map, key, 0)); + assertEquals("Wrong value in map", value, map.get(key)); + } + + /** + * Tests putIfAbsent() if the map does not contain the key in question. + */ + @Test + public void testPutIfAbsentKeyNotPresent() { + final String key = "testKey"; + final Integer value = 42; + ConcurrentMap map = new ConcurrentHashMap(); + assertEquals("Wrong result", value, + ConcurrentUtils.putIfAbsent(map, key, value)); + assertEquals("Wrong value in map", value, map.get(key)); + } + + /** + * Tests putIfAbsent() if a null map is passed in. + */ + @Test + public void testPutIfAbsentNullMap() { + assertNull("Wrong result", + ConcurrentUtils.putIfAbsent(null, "test", 100)); + } + + /** + * Tests createIfAbsent() if the key is found in the map. + */ + @Test + public void testCreateIfAbsentKeyPresent() throws ConcurrentException { + @SuppressWarnings("unchecked") + ConcurrentInitializer init = EasyMock + .createMock(ConcurrentInitializer.class); + EasyMock.replay(init); + final String key = "testKey"; + final Integer value = 42; + ConcurrentMap map = new ConcurrentHashMap(); + map.put(key, value); + assertEquals("Wrong result", value, + ConcurrentUtils.createIfAbsent(map, key, init)); + assertEquals("Wrong value in map", value, map.get(key)); + EasyMock.verify(init); + } + + /** + * Tests createIfAbsent() if the map does not contain the key in question. + */ + @Test + public void testCreateIfAbsentKeyNotPresent() throws ConcurrentException { + @SuppressWarnings("unchecked") + ConcurrentInitializer init = EasyMock + .createMock(ConcurrentInitializer.class); + final String key = "testKey"; + final Integer value = 42; + EasyMock.expect(init.get()).andReturn(value); + EasyMock.replay(init); + ConcurrentMap map = new ConcurrentHashMap(); + assertEquals("Wrong result", value, + ConcurrentUtils.createIfAbsent(map, key, init)); + assertEquals("Wrong value in map", value, map.get(key)); + EasyMock.verify(init); + } + + /** + * Tests createIfAbsent() if a null map is passed in. + */ + @Test + public void testCreateIfAbsentNullMap() throws ConcurrentException { + @SuppressWarnings("unchecked") + ConcurrentInitializer init = EasyMock + .createMock(ConcurrentInitializer.class); + EasyMock.replay(init); + assertNull("Wrong result", + ConcurrentUtils.createIfAbsent(null, "test", init)); + EasyMock.verify(init); + } + + /** + * Tests createIfAbsent() if a null initializer is passed in. + */ + @Test + public void testCreateIfAbsentNullInit() throws ConcurrentException { + ConcurrentMap map = new ConcurrentHashMap(); + final String key = "testKey"; + final Integer value = 42; + map.put(key, value); + assertNull("Wrong result", + ConcurrentUtils.createIfAbsent(map, key, null)); + assertEquals("Map was changed", value, map.get(key)); + } + + /** + * Tests createIfAbsentUnchecked() if no exception is thrown. + */ + @Test + public void testCreateIfAbsentUncheckedSuccess() { + final String key = "testKey"; + final Integer value = 42; + ConcurrentMap map = new ConcurrentHashMap(); + assertEquals("Wrong result", value, + ConcurrentUtils.createIfAbsentUnchecked(map, key, + new ConstantInitializer(value))); + assertEquals("Wrong value in map", value, map.get(key)); + } + + /** + * Tests createIfAbsentUnchecked() if an exception is thrown. + */ + @Test + public void testCreateIfAbsentUncheckedException() + throws ConcurrentException { + @SuppressWarnings("unchecked") + ConcurrentInitializer init = EasyMock + .createMock(ConcurrentInitializer.class); + Exception ex = new Exception(); + EasyMock.expect(init.get()).andThrow(new ConcurrentException(ex)); + EasyMock.replay(init); + try { + ConcurrentUtils.createIfAbsentUnchecked( + new ConcurrentHashMap(), "test", init); + fail("Exception not thrown!"); + } catch (ConcurrentRuntimeException crex) { + assertEquals("Wrong cause", ex, crex.getCause()); + } + EasyMock.verify(init); + } +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/concurrent/ConstantInitializer.java b/ApacheCommonsLang/org/apache/commons/lang3/concurrent/ConstantInitializer.java new file mode 100644 index 0000000..c0a792d --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/concurrent/ConstantInitializer.java @@ -0,0 +1,129 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.concurrent; + +import org.apache.commons.lang3.ObjectUtils; + +/** + *

+ * A very simple implementation of the {@link ConcurrentInitializer} interface + * which always returns the same object. + *

+ *

+ * An instance of this class is passed a reference to an object when it is + * constructed. The {@link #get()} method just returns this object. No + * synchronization is required. + *

+ *

+ * This class is useful for instance for unit testing or in cases where a + * specific object has to be passed to an object which expects a + * {@link ConcurrentInitializer}. + *

+ * + * @since 3.0 + * @version $Id: ConstantInitializer.java 1309977 2012-04-05 17:53:39Z ggregory $ + * @param the type of the object managed by this initializer + */ +public class ConstantInitializer implements ConcurrentInitializer { + /** Constant for the format of the string representation. */ + private static final String FMT_TO_STRING = "ConstantInitializer@%d [ object = %s ]"; + + /** Stores the managed object. */ + private final T object; + + /** + * Creates a new instance of {@code ConstantInitializer} and initializes it + * with the object to be managed. The {@code get()} method will always + * return the object passed here. This class does not place any restrictions + * on the object. It may be null, then {@code get()} will return + * null, too. + * + * @param obj the object to be managed by this initializer + */ + public ConstantInitializer(T obj) { + object = obj; + } + + /** + * Directly returns the object that was passed to the constructor. This is + * the same object as returned by {@code get()}. However, this method does + * not declare that it throws an exception. + * + * @return the object managed by this initializer + */ + public final T getObject() { + return object; + } + + /** + * Returns the object managed by this initializer. This implementation just + * returns the object passed to the constructor. + * + * @return the object managed by this initializer + * @throws ConcurrentException if an error occurs + */ + @Override + public T get() throws ConcurrentException { + return getObject(); + } + + /** + * Returns a hash code for this object. This implementation returns the hash + * code of the managed object. + * + * @return a hash code for this object + */ + @Override + public int hashCode() { + return getObject() != null ? getObject().hashCode() : 0; + } + + /** + * Compares this object with another one. This implementation returns + * true if and only if the passed in object is an instance of + * {@code ConstantInitializer} which refers to an object equals to the + * object managed by this instance. + * + * @param obj the object to compare to + * @return a flag whether the objects are equal + */ + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof ConstantInitializer)) { + return false; + } + + ConstantInitializer c = (ConstantInitializer) obj; + return ObjectUtils.equals(getObject(), c.getObject()); + } + + /** + * Returns a string representation for this object. This string also + * contains a string representation of the object managed by this + * initializer. + * + * @return a string for this object + */ + @Override + public String toString() { + return String.format(FMT_TO_STRING, Integer.valueOf(System.identityHashCode(this)), + String.valueOf(getObject())); + } +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/concurrent/ConstantInitializerTest.java b/ApacheCommonsLang/org/apache/commons/lang3/concurrent/ConstantInitializerTest.java new file mode 100644 index 0000000..de83f1d --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/concurrent/ConstantInitializerTest.java @@ -0,0 +1,133 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.concurrent; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.regex.Pattern; + +import org.junit.Before; +import org.junit.Test; + +/** + * Test class for {@code ConstantInitializer}. + * + * @version $Id: ConstantInitializerTest.java 1206700 2011-11-27 11:39:35Z sebb $ + */ +public class ConstantInitializerTest { + /** Constant for the object managed by the initializer. */ + private static final Integer VALUE = 42; + + /** The initializer to be tested. */ + private ConstantInitializer init; + + @Before + public void setUp() throws Exception { + init = new ConstantInitializer(VALUE); + } + + /** + * Helper method for testing equals() and hashCode(). + * + * @param obj the object to compare with the test instance + * @param expected the expected result + */ + private void checkEquals(Object obj, boolean expected) { + assertTrue("Wrong result of equals", expected == init.equals(obj)); + if (obj != null) { + assertTrue("Not symmetric", expected == obj.equals(init)); + if (expected) { + assertEquals("Different hash codes", init.hashCode(), + obj.hashCode()); + } + } + } + + /** + * Tests whether the correct object is returned. + */ + @Test + public void testGetObject() { + assertEquals("Wrong object", VALUE, init.getObject()); + } + + /** + * Tests whether get() returns the correct object. + */ + @Test + public void testGet() throws ConcurrentException { + assertEquals("Wrong object", VALUE, init.get()); + } + + /** + * Tests equals() if the expected result is true. + */ + @Test + public void testEqualsTrue() { + checkEquals(init, true); + ConstantInitializer init2 = new ConstantInitializer( + Integer.valueOf(VALUE.intValue())); + checkEquals(init2, true); + init = new ConstantInitializer(null); + init2 = new ConstantInitializer(null); + checkEquals(init2, true); + } + + /** + * Tests equals() if the expected result is false. + */ + @Test + public void testEqualsFalse() { + ConstantInitializer init2 = new ConstantInitializer( + null); + checkEquals(init2, false); + init2 = new ConstantInitializer(VALUE + 1); + checkEquals(init2, false); + } + + /** + * Tests equals() with objects of other classes. + */ + @Test + public void testEqualsWithOtherObjects() { + checkEquals(null, false); + checkEquals(this, false); + checkEquals(new ConstantInitializer("Test"), false); + } + + /** + * Tests the string representation. + */ + @Test + public void testToString() { + String s = init.toString(); + Pattern pattern = Pattern + .compile("ConstantInitializer@\\d+ \\[ object = " + VALUE + + " \\]"); + assertTrue("Wrong string: " + s, pattern.matcher(s).matches()); + } + + /** + * Tests the string representation if the managed object is null. + */ + @Test + public void testToStringNull() { + String s = new ConstantInitializer(null).toString(); + assertTrue("Object not found: " + s, s.indexOf("object = null") > 0); + } +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/concurrent/LazyInitializer.java b/ApacheCommonsLang/org/apache/commons/lang3/concurrent/LazyInitializer.java new file mode 100644 index 0000000..7cd6317 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/concurrent/LazyInitializer.java @@ -0,0 +1,120 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.concurrent; + +/** + *

+ * This class provides a generic implementation of the lazy initialization + * pattern. + *

+ *

+ * Sometimes an application has to deal with an object only under certain + * circumstances, e.g. when the user selects a specific menu item or if a + * special event is received. If the creation of the object is costly or the + * consumption of memory or other system resources is significant, it may make + * sense to defer the creation of this object until it is really needed. This is + * a use case for the lazy initialization pattern. + *

+ *

+ * This abstract base class provides an implementation of the double-check idiom + * for an instance field as discussed in Joshua Bloch's "Effective Java", 2nd + * edition, item 71. The class already implements all necessary synchronization. + * A concrete subclass has to implement the {@code initialize()} method, which + * actually creates the wrapped data object. + *

+ *

+ * As an usage example consider that we have a class {@code ComplexObject} whose + * instantiation is a complex operation. In order to apply lazy initialization + * to this class, a subclass of {@code LazyInitializer} has to be created: + * + *

+ * public class ComplexObjectInitializer extends LazyInitializer<ComplexObject> {
+ *     @Override
+ *     protected ComplexObject initialize() {
+ *         return new ComplexObject();
+ *     }
+ * }
+ * 
+ * + * Access to the data object is provided through the {@code get()} method. So, + * code that wants to obtain the {@code ComplexObject} instance would simply + * look like this: + * + *
+ * // Create an instance of the lazy initializer
+ * ComplexObjectInitializer initializer = new ComplexObjectInitializer();
+ * ...
+ * // When the object is actually needed:
+ * ComplexObject cobj = initializer.get();
+ * 
+ * + *

+ *

+ * If multiple threads call the {@code get()} method when the object has not yet + * been created, they are blocked until initialization completes. The algorithm + * guarantees that only a single instance of the wrapped object class is + * created, which is passed to all callers. Once initialized, calls to the + * {@code get()} method are pretty fast because no synchronization is needed + * (only an access to a volatile member field). + *

+ * + * @since 3.0 + * @version $Id: LazyInitializer.java 1309977 2012-04-05 17:53:39Z ggregory $ + * @param the type of the object managed by this initializer class + */ +public abstract class LazyInitializer implements ConcurrentInitializer { + /** Stores the managed object. */ + private volatile T object; + + /** + * Returns the object wrapped by this instance. On first access the object + * is created. After that it is cached and can be accessed pretty fast. + * + * @return the object initialized by this {@code LazyInitializer} + * @throws ConcurrentException if an error occurred during initialization of + * the object + */ + @Override + public T get() throws ConcurrentException { + // use a temporary variable to reduce the number of reads of the + // volatile field + T result = object; + + if (result == null) { + synchronized (this) { + result = object; + if (result == null) { + object = result = initialize(); + } + } + } + + return result; + } + + /** + * Creates and initializes the object managed by this {@code + * LazyInitializer}. This method is called by {@link #get()} when the object + * is accessed for the first time. An implementation can focus on the + * creation of the object. No synchronization is needed, as this is already + * handled by {@code get()}. + * + * @return the managed data object + * @throws ConcurrentException if an error occurs during object creation + */ + protected abstract T initialize() throws ConcurrentException; +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/concurrent/LazyInitializerTest.java b/ApacheCommonsLang/org/apache/commons/lang3/concurrent/LazyInitializerTest.java new file mode 100644 index 0000000..e7723d6 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/concurrent/LazyInitializerTest.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.concurrent; + +import org.junit.Before; + +/** + * Test class for {@code LazyInitializer}. + * + * @version $Id: LazyInitializerTest.java 929189 2010-03-30 16:49:22Z oheger $ + */ +public class LazyInitializerTest extends AbstractConcurrentInitializerTest { + /** The initializer to be tested. */ + private LazyInitializerTestImpl initializer; + + @Before + public void setUp() throws Exception { + initializer = new LazyInitializerTestImpl(); + } + + /** + * Returns the initializer to be tested. This implementation returns the + * {@code LazyInitializer} created in the {@code setUp()} method. + * + * @return the initializer to be tested + */ + @Override + protected ConcurrentInitializer createInitializer() { + return initializer; + } + + /** + * A test implementation of LazyInitializer. This class creates a plain + * Object. As Object does not provide a specific equals() method, it is easy + * to check whether multiple instances were created. + */ + private static class LazyInitializerTestImpl extends + LazyInitializer { + @Override + protected Object initialize() { + return new Object(); + } + } +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/concurrent/MultiBackgroundInitializer.java b/ApacheCommonsLang/org/apache/commons/lang3/concurrent/MultiBackgroundInitializer.java new file mode 100644 index 0000000..e13c544 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/concurrent/MultiBackgroundInitializer.java @@ -0,0 +1,352 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.concurrent; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.concurrent.ExecutorService; + +/** + *

+ * A specialized {@link BackgroundInitializer} implementation that can deal with + * multiple background initialization tasks. + *

+ *

+ * This class has a similar purpose as {@link BackgroundInitializer}. However, + * it is not limited to a single background initialization task. Rather it + * manages an arbitrary number of {@code BackgroundInitializer} objects, + * executes them, and waits until they are completely initialized. This is + * useful for applications that have to perform multiple initialization tasks + * that can run in parallel (i.e. that do not depend on each other). This class + * takes care about the management of an {@code ExecutorService} and shares it + * with the {@code BackgroundInitializer} objects it is responsible for; so the + * using application need not bother with these details. + *

+ *

+ * The typical usage scenario for {@code MultiBackgroundInitializer} is as + * follows: + *

    + *
  • Create a new instance of the class. Optionally pass in a pre-configured + * {@code ExecutorService}. Alternatively {@code MultiBackgroundInitializer} can + * create a temporary {@code ExecutorService} and delete it after initialization + * is complete.
  • + *
  • Create specialized {@link BackgroundInitializer} objects for the + * initialization tasks to be performed and add them to the {@code + * MultiBackgroundInitializer} using the + * {@link #addInitializer(String, BackgroundInitializer)} method.
  • + *
  • After all initializers have been added, call the {@link #start()} method. + *
  • + *
  • When access to the result objects produced by the {@code + * BackgroundInitializer} objects is needed call the {@link #get()} method. The + * object returned here provides access to all result objects created during + * initialization. It also stores information about exceptions that have + * occurred.
  • + *
+ *

+ *

+ * {@code MultiBackgroundInitializer} starts a special controller task that + * starts all {@code BackgroundInitializer} objects added to the instance. + * Before the an initializer is started it is checked whether this initializer + * already has an {@code ExecutorService} set. If this is the case, this {@code + * ExecutorService} is used for running the background task. Otherwise the + * current {@code ExecutorService} of this {@code MultiBackgroundInitializer} is + * shared with the initializer. + *

+ *

+ * The easiest way of using this class is to let it deal with the management of + * an {@code ExecutorService} itself: If no external {@code ExecutorService} is + * provided, the class creates a temporary {@code ExecutorService} (that is + * capable of executing all background tasks in parallel) and destroys it at the + * end of background processing. + *

+ *

+ * Alternatively an external {@code ExecutorService} can be provided - either at + * construction time or later by calling the + * {@link #setExternalExecutor(ExecutorService)} method. In this case all + * background tasks are scheduled at this external {@code ExecutorService}. + * Important note: When using an external {@code + * ExecutorService} be sure that the number of threads managed by the service is + * large enough. Otherwise a deadlock can happen! This is the case in the + * following scenario: {@code MultiBackgroundInitializer} starts a task that + * starts all registered {@code BackgroundInitializer} objects and waits for + * their completion. If for instance a single threaded {@code ExecutorService} + * is used, none of the background tasks can be executed, and the task created + * by {@code MultiBackgroundInitializer} waits forever. + *

+ * + * @since 3.0 + * @version $Id: MultiBackgroundInitializer.java 1082301 2011-03-16 21:02:15Z oheger $ + */ +public class MultiBackgroundInitializer + extends + BackgroundInitializer { + /** A map with the child initializers. */ + private final Map> childInitializers = + new HashMap>(); + + /** + * Creates a new instance of {@code MultiBackgroundInitializer}. + */ + public MultiBackgroundInitializer() { + super(); + } + + /** + * Creates a new instance of {@code MultiBackgroundInitializer} and + * initializes it with the given external {@code ExecutorService}. + * + * @param exec the {@code ExecutorService} for executing the background + * tasks + */ + public MultiBackgroundInitializer(ExecutorService exec) { + super(exec); + } + + /** + * Adds a new {@code BackgroundInitializer} to this object. When this + * {@code MultiBackgroundInitializer} is started, the given initializer will + * be processed. This method must not be called after {@link #start()} has + * been invoked. + * + * @param name the name of the initializer (must not be null) + * @param init the {@code BackgroundInitializer} to add (must not be + * null) + * @throws IllegalArgumentException if a required parameter is missing + * @throws IllegalStateException if {@code start()} has already been called + */ + public void addInitializer(String name, BackgroundInitializer init) { + if (name == null) { + throw new IllegalArgumentException( + "Name of child initializer must not be null!"); + } + if (init == null) { + throw new IllegalArgumentException( + "Child initializer must not be null!"); + } + + synchronized (this) { + if (isStarted()) { + throw new IllegalStateException( + "addInitializer() must not be called after start()!"); + } + childInitializers.put(name, init); + } + } + + /** + * Returns the number of tasks needed for executing all child {@code + * BackgroundInitializer} objects in parallel. This implementation sums up + * the required tasks for all child initializers (which is necessary if one + * of the child initializers is itself a {@code MultiBackgroundInitializer} + * ). Then it adds 1 for the control task that waits for the completion of + * the children. + * + * @return the number of tasks required for background processing + */ + @Override + protected int getTaskCount() { + int result = 1; + + for (BackgroundInitializer bi : childInitializers.values()) { + result += bi.getTaskCount(); + } + + return result; + } + + /** + * Creates the results object. This implementation starts all child {@code + * BackgroundInitializer} objects. Then it collects their results and + * creates a {@code MultiBackgroundInitializerResults} object with this + * data. If a child initializer throws a checked exceptions, it is added to + * the results object. Unchecked exceptions are propagated. + * + * @return the results object + * @throws Exception if an error occurs + */ + @Override + protected MultiBackgroundInitializerResults initialize() throws Exception { + Map> inits; + synchronized (this) { + // create a snapshot to operate on + inits = new HashMap>( + childInitializers); + } + + // start the child initializers + ExecutorService exec = getActiveExecutor(); + for (BackgroundInitializer bi : inits.values()) { + if (bi.getExternalExecutor() == null) { + // share the executor service if necessary + bi.setExternalExecutor(exec); + } + bi.start(); + } + + // collect the results + Map results = new HashMap(); + Map excepts = new HashMap(); + for (Map.Entry> e : inits.entrySet()) { + try { + results.put(e.getKey(), e.getValue().get()); + } catch (ConcurrentException cex) { + excepts.put(e.getKey(), cex); + } + } + + return new MultiBackgroundInitializerResults(inits, results, excepts); + } + + /** + * A data class for storing the results of the background initialization + * performed by {@code MultiBackgroundInitializer}. Objects of this inner + * class are returned by {@link MultiBackgroundInitializer#initialize()}. + * They allow access to all result objects produced by the + * {@link BackgroundInitializer} objects managed by the owning instance. It + * is also possible to retrieve status information about single + * {@link BackgroundInitializer}s, i.e. whether they completed normally or + * caused an exception. + */ + public static class MultiBackgroundInitializerResults { + /** A map with the child initializers. */ + private final Map> initializers; + + /** A map with the result objects. */ + private final Map resultObjects; + + /** A map with the exceptions. */ + private final Map exceptions; + + /** + * Creates a new instance of {@code MultiBackgroundInitializerResults} + * and initializes it with maps for the {@code BackgroundInitializer} + * objects, their result objects and the exceptions thrown by them. + * + * @param inits the {@code BackgroundInitializer} objects + * @param results the result objects + * @param excepts the exceptions + */ + private MultiBackgroundInitializerResults( + Map> inits, + Map results, + Map excepts) { + initializers = inits; + resultObjects = results; + exceptions = excepts; + } + + /** + * Returns the {@code BackgroundInitializer} with the given name. If the + * name cannot be resolved, an exception is thrown. + * + * @param name the name of the {@code BackgroundInitializer} + * @return the {@code BackgroundInitializer} with this name + * @throws NoSuchElementException if the name cannot be resolved + */ + public BackgroundInitializer getInitializer(String name) { + return checkName(name); + } + + /** + * Returns the result object produced by the {@code + * BackgroundInitializer} with the given name. This is the object + * returned by the initializer's {@code initialize()} method. If this + * {@code BackgroundInitializer} caused an exception, null is + * returned. If the name cannot be resolved, an exception is thrown. + * + * @param name the name of the {@code BackgroundInitializer} + * @return the result object produced by this {@code + * BackgroundInitializer} + * @throws NoSuchElementException if the name cannot be resolved + */ + public Object getResultObject(String name) { + checkName(name); + return resultObjects.get(name); + } + + /** + * Returns a flag whether the {@code BackgroundInitializer} with the + * given name caused an exception. + * + * @param name the name of the {@code BackgroundInitializer} + * @return a flag whether this initializer caused an exception + * @throws NoSuchElementException if the name cannot be resolved + */ + public boolean isException(String name) { + checkName(name); + return exceptions.containsKey(name); + } + + /** + * Returns the {@code ConcurrentException} object that was thrown by the + * {@code BackgroundInitializer} with the given name. If this + * initializer did not throw an exception, the return value is + * null. If the name cannot be resolved, an exception is thrown. + * + * @param name the name of the {@code BackgroundInitializer} + * @return the exception thrown by this initializer + * @throws NoSuchElementException if the name cannot be resolved + */ + public ConcurrentException getException(String name) { + checkName(name); + return exceptions.get(name); + } + + /** + * Returns a set with the names of all {@code BackgroundInitializer} + * objects managed by the {@code MultiBackgroundInitializer}. + * + * @return an (unmodifiable) set with the names of the managed {@code + * BackgroundInitializer} objects + */ + public Set initializerNames() { + return Collections.unmodifiableSet(initializers.keySet()); + } + + /** + * Returns a flag whether the whole initialization was successful. This + * is the case if no child initializer has thrown an exception. + * + * @return a flag whether the initialization was successful + */ + public boolean isSuccessful() { + return exceptions.isEmpty(); + } + + /** + * Checks whether an initializer with the given name exists. If not, + * throws an exception. If it exists, the associated child initializer + * is returned. + * + * @param name the name to check + * @return the initializer with this name + * @throws NoSuchElementException if the name is unknown + */ + private BackgroundInitializer checkName(String name) { + BackgroundInitializer init = initializers.get(name); + if (init == null) { + throw new NoSuchElementException( + "No child initializer with name " + name); + } + + return init; + } + } +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/concurrent/MultiBackgroundInitializerTest.java b/ApacheCommonsLang/org/apache/commons/lang3/concurrent/MultiBackgroundInitializerTest.java new file mode 100644 index 0000000..a825050 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/concurrent/MultiBackgroundInitializerTest.java @@ -0,0 +1,381 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.concurrent; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import org.junit.Before; +import org.junit.Test; + +/** + * Test class for {@link MultiBackgroundInitializer}. + * + * @version $Id: MultiBackgroundInitializerTest.java 987621 2010-08-20 20:01:10Z oheger $ + */ +public class MultiBackgroundInitializerTest { + /** Constant for the names of the child initializers. */ + private static final String CHILD_INIT = "childInitializer"; + + /** The initializer to be tested. */ + private MultiBackgroundInitializer initializer; + + @Before + public void setUp() throws Exception { + initializer = new MultiBackgroundInitializer(); + } + + /** + * Tests whether a child initializer has been executed. Optionally the + * expected executor service can be checked, too. + * + * @param child the child initializer + * @param expExec the expected executor service (null if the executor should + * not be checked) + * @throws ConcurrentException if an error occurs + */ + private void checkChild(BackgroundInitializer child, + ExecutorService expExec) throws ConcurrentException { + ChildBackgroundInitializer cinit = (ChildBackgroundInitializer) child; + Integer result = cinit.get(); + assertEquals("Wrong result", 1, result.intValue()); + assertEquals("Wrong number of executions", 1, cinit.initializeCalls); + if (expExec != null) { + assertEquals("Wrong executor service", expExec, + cinit.currentExecutor); + } + } + + /** + * Tests addInitializer() if a null name is passed in. This should cause an + * exception. + */ + @Test(expected = IllegalArgumentException.class) + public void testAddInitializerNullName() { + initializer.addInitializer(null, new ChildBackgroundInitializer()); + } + + /** + * Tests addInitializer() if a null initializer is passed in. This should + * cause an exception. + */ + @Test(expected = IllegalArgumentException.class) + public void testAddInitializerNullInit() { + initializer.addInitializer(CHILD_INIT, null); + } + + /** + * Tests the background processing if there are no child initializers. + */ + @Test + public void testInitializeNoChildren() throws ConcurrentException { + assertTrue("Wrong result of start()", initializer.start()); + MultiBackgroundInitializer.MultiBackgroundInitializerResults res = initializer + .get(); + assertTrue("Got child initializers", res.initializerNames().isEmpty()); + assertTrue("Executor not shutdown", initializer.getActiveExecutor() + .isShutdown()); + } + + /** + * Helper method for testing the initialize() method. This method can + * operate with both an external and a temporary executor service. + * + * @return the result object produced by the initializer + */ + private MultiBackgroundInitializer.MultiBackgroundInitializerResults checkInitialize() + throws ConcurrentException { + final int count = 5; + for (int i = 0; i < count; i++) { + initializer.addInitializer(CHILD_INIT + i, + new ChildBackgroundInitializer()); + } + initializer.start(); + MultiBackgroundInitializer.MultiBackgroundInitializerResults res = initializer + .get(); + assertEquals("Wrong number of child initializers", count, res + .initializerNames().size()); + for (int i = 0; i < count; i++) { + String key = CHILD_INIT + i; + assertTrue("Name not found: " + key, res.initializerNames() + .contains(key)); + assertEquals("Wrong result object", Integer.valueOf(1), res + .getResultObject(key)); + assertFalse("Exception flag", res.isException(key)); + assertNull("Got an exception", res.getException(key)); + checkChild(res.getInitializer(key), initializer.getActiveExecutor()); + } + return res; + } + + /** + * Tests background processing if a temporary executor is used. + */ + @Test + public void testInitializeTempExec() throws ConcurrentException { + checkInitialize(); + assertTrue("Executor not shutdown", initializer.getActiveExecutor() + .isShutdown()); + } + + /** + * Tests background processing if an external executor service is provided. + */ + @Test + public void testInitializeExternalExec() throws ConcurrentException { + ExecutorService exec = Executors.newCachedThreadPool(); + try { + initializer = new MultiBackgroundInitializer(exec); + checkInitialize(); + assertEquals("Wrong executor", exec, initializer + .getActiveExecutor()); + assertFalse("Executor was shutdown", exec.isShutdown()); + } finally { + exec.shutdown(); + } + } + + /** + * Tests the behavior of initialize() if a child initializer has a specific + * executor service. Then this service should not be overridden. + */ + @Test + public void testInitializeChildWithExecutor() throws ConcurrentException { + final String initExec = "childInitializerWithExecutor"; + ExecutorService exec = Executors.newSingleThreadExecutor(); + try { + ChildBackgroundInitializer c1 = new ChildBackgroundInitializer(); + ChildBackgroundInitializer c2 = new ChildBackgroundInitializer(); + c2.setExternalExecutor(exec); + initializer.addInitializer(CHILD_INIT, c1); + initializer.addInitializer(initExec, c2); + initializer.start(); + initializer.get(); + checkChild(c1, initializer.getActiveExecutor()); + checkChild(c2, exec); + } finally { + exec.shutdown(); + } + } + + /** + * Tries to add another child initializer after the start() method has been + * called. This should not be allowed. + */ + @Test + public void testAddInitializerAfterStart() throws ConcurrentException { + initializer.start(); + try { + initializer.addInitializer(CHILD_INIT, + new ChildBackgroundInitializer()); + fail("Could add initializer after start()!"); + } catch (IllegalStateException istex) { + initializer.get(); + } + } + + /** + * Tries to query an unknown child initializer from the results object. This + * should cause an exception. + */ + @Test(expected = NoSuchElementException.class) + public void testResultGetInitializerUnknown() throws ConcurrentException { + MultiBackgroundInitializer.MultiBackgroundInitializerResults res = checkInitialize(); + res.getInitializer("unknown"); + } + + /** + * Tries to query the results of an unknown child initializer from the + * results object. This should cause an exception. + */ + @Test(expected = NoSuchElementException.class) + public void testResultGetResultObjectUnknown() throws ConcurrentException { + MultiBackgroundInitializer.MultiBackgroundInitializerResults res = checkInitialize(); + res.getResultObject("unknown"); + } + + /** + * Tries to query the exception of an unknown child initializer from the + * results object. This should cause an exception. + */ + @Test(expected = NoSuchElementException.class) + public void testResultGetExceptionUnknown() throws ConcurrentException { + MultiBackgroundInitializer.MultiBackgroundInitializerResults res = checkInitialize(); + res.getException("unknown"); + } + + /** + * Tries to query the exception flag of an unknown child initializer from + * the results object. This should cause an exception. + */ + @Test(expected = NoSuchElementException.class) + public void testResultIsExceptionUnknown() throws ConcurrentException { + MultiBackgroundInitializer.MultiBackgroundInitializerResults res = checkInitialize(); + res.isException("unknown"); + } + + /** + * Tests that the set with the names of the initializers cannot be modified. + */ + @Test(expected = UnsupportedOperationException.class) + public void testResultInitializerNamesModify() throws ConcurrentException { + checkInitialize(); + MultiBackgroundInitializer.MultiBackgroundInitializerResults res = initializer + .get(); + Iterator it = res.initializerNames().iterator(); + it.next(); + it.remove(); + } + + /** + * Tests the behavior of the initializer if one of the child initializers + * throws a runtime exception. + */ + @Test + public void testInitializeRuntimeEx() { + ChildBackgroundInitializer child = new ChildBackgroundInitializer(); + child.ex = new RuntimeException(); + initializer.addInitializer(CHILD_INIT, child); + initializer.start(); + try { + initializer.get(); + fail("Runtime exception not thrown!"); + } catch (Exception ex) { + assertEquals("Wrong exception", child.ex, ex); + } + } + + /** + * Tests the behavior of the initializer if one of the child initializers + * throws a checked exception. + */ + @Test + public void testInitializeEx() throws ConcurrentException { + ChildBackgroundInitializer child = new ChildBackgroundInitializer(); + child.ex = new Exception(); + initializer.addInitializer(CHILD_INIT, child); + initializer.start(); + MultiBackgroundInitializer.MultiBackgroundInitializerResults res = initializer + .get(); + assertTrue("No exception flag", res.isException(CHILD_INIT)); + assertNull("Got a results object", res.getResultObject(CHILD_INIT)); + ConcurrentException cex = res.getException(CHILD_INIT); + assertEquals("Wrong cause", child.ex, cex.getCause()); + } + + /** + * Tests the isSuccessful() method of the result object if no child + * initializer has thrown an exception. + */ + @Test + public void testInitializeResultsIsSuccessfulTrue() + throws ConcurrentException { + ChildBackgroundInitializer child = new ChildBackgroundInitializer(); + initializer.addInitializer(CHILD_INIT, child); + initializer.start(); + MultiBackgroundInitializer.MultiBackgroundInitializerResults res = initializer + .get(); + assertTrue("Wrong success flag", res.isSuccessful()); + } + + /** + * Tests the isSuccessful() method of the result object if at least one + * child initializer has thrown an exception. + */ + @Test + public void testInitializeResultsIsSuccessfulFalse() + throws ConcurrentException { + ChildBackgroundInitializer child = new ChildBackgroundInitializer(); + child.ex = new Exception(); + initializer.addInitializer(CHILD_INIT, child); + initializer.start(); + MultiBackgroundInitializer.MultiBackgroundInitializerResults res = initializer + .get(); + assertFalse("Wrong success flag", res.isSuccessful()); + } + + /** + * Tests whether MultiBackgroundInitializers can be combined in a nested + * way. + */ + @Test + public void testInitializeNested() throws ConcurrentException { + final String nameMulti = "multiChildInitializer"; + initializer + .addInitializer(CHILD_INIT, new ChildBackgroundInitializer()); + MultiBackgroundInitializer mi2 = new MultiBackgroundInitializer(); + final int count = 3; + for (int i = 0; i < count; i++) { + mi2 + .addInitializer(CHILD_INIT + i, + new ChildBackgroundInitializer()); + } + initializer.addInitializer(nameMulti, mi2); + initializer.start(); + MultiBackgroundInitializer.MultiBackgroundInitializerResults res = initializer + .get(); + ExecutorService exec = initializer.getActiveExecutor(); + checkChild(res.getInitializer(CHILD_INIT), exec); + MultiBackgroundInitializer.MultiBackgroundInitializerResults res2 = (MultiBackgroundInitializer.MultiBackgroundInitializerResults) res + .getResultObject(nameMulti); + assertEquals("Wrong number of initializers", count, res2 + .initializerNames().size()); + for (int i = 0; i < count; i++) { + checkChild(res2.getInitializer(CHILD_INIT + i), exec); + } + assertTrue("Executor not shutdown", exec.isShutdown()); + } + + /** + * A concrete implementation of {@code BackgroundInitializer} used for + * defining background tasks for {@code MultiBackgroundInitializer}. + */ + private static class ChildBackgroundInitializer extends + BackgroundInitializer { + /** Stores the current executor service. */ + volatile ExecutorService currentExecutor; + + /** A counter for the invocations of initialize(). */ + volatile int initializeCalls; + + /** An exception to be thrown by initialize(). */ + Exception ex; + + /** + * Records this invocation. Optionally throws an exception. + */ + @Override + protected Integer initialize() throws Exception { + currentExecutor = getActiveExecutor(); + initializeCalls++; + + if (ex != null) { + throw ex; + } + + return initializeCalls; + } + } +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/concurrent/TimedSemaphore.java b/ApacheCommonsLang/org/apache/commons/lang3/concurrent/TimedSemaphore.java new file mode 100644 index 0000000..ccb0458 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/concurrent/TimedSemaphore.java @@ -0,0 +1,422 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.concurrent; + +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +/** + *

+ * A specialized semaphore implementation that provides a number of + * permits in a given time frame. + *

+ *

+ * This class is similar to the {@code java.util.concurrent.Semaphore} class + * provided by the JDK in that it manages a configurable number of permits. + * Using the {@link #acquire()} method a permit can be requested by a thread. + * However, there is an additional timing dimension: there is no {@code + * release()} method for freeing a permit, but all permits are automatically + * released at the end of a configurable time frame. If a thread calls + * {@link #acquire()} and the available permits are already exhausted for this + * time frame, the thread is blocked. When the time frame ends all permits + * requested so far are restored, and blocking threads are waked up again, so + * that they can try to acquire a new permit. This basically means that in the + * specified time frame only the given number of operations is possible. + *

+ *

+ * A use case for this class is to artificially limit the load produced by a + * process. As an example consider an application that issues database queries + * on a production system in a background process to gather statistical + * information. This background processing should not produce so much database + * load that the functionality and the performance of the production system are + * impacted. Here a {@code TimedSemaphore} could be installed to guarantee that + * only a given number of database queries are issued per second. + *

+ *

+ * A thread class for performing database queries could look as follows: + * + *

+ * public class StatisticsThread extends Thread {
+ *     // The semaphore for limiting database load.
+ *     private final TimedSemaphore semaphore;
+ *     // Create an instance and set the semaphore
+ *     public StatisticsThread(TimedSemaphore timedSemaphore) {
+ *         semaphore = timedSemaphore;
+ *     }
+ *     // Gather statistics
+ *     public void run() {
+ *         try {
+ *             while(true) {
+ *                 semaphore.acquire();   // limit database load
+ *                 performQuery();        // issue a query
+ *             }
+ *         } catch(InterruptedException) {
+ *             // fall through
+ *         }
+ *     }
+ *     ...
+ * }
+ * 
+ * + * The following code fragment shows how a {@code TimedSemaphore} is created + * that allows only 10 operations per second and passed to the statistics + * thread: + * + *
+ * TimedSemaphore sem = new TimedSemaphore(1, TimeUnit.SECOND, 10);
+ * StatisticsThread thread = new StatisticsThread(sem);
+ * thread.start();
+ * 
+ * + *

+ *

+ * When creating an instance the time period for the semaphore must be + * specified. {@code TimedSemaphore} uses an executor service with a + * corresponding period to monitor this interval. The {@code + * ScheduledExecutorService} to be used for this purpose can be provided at + * construction time. Alternatively the class creates an internal executor + * service. + *

+ *

+ * Client code that uses {@code TimedSemaphore} has to call the + * {@link #acquire()} method in aach processing step. {@code TimedSemaphore} + * keeps track of the number of invocations of the {@link #acquire()} method and + * blocks the calling thread if the counter exceeds the limit specified. When + * the timer signals the end of the time period the counter is reset and all + * waiting threads are released. Then another cycle can start. + *

+ *

+ * It is possible to modify the limit at any time using the + * {@link #setLimit(int)} method. This is useful if the load produced by an + * operation has to be adapted dynamically. In the example scenario with the + * thread collecting statistics it may make sense to specify a low limit during + * day time while allowing a higher load in the night time. Reducing the limit + * takes effect immediately by blocking incoming callers. If the limit is + * increased, waiting threads are not released immediately, but wake up when the + * timer runs out. Then, in the next period more processing steps can be + * performed without blocking. By setting the limit to 0 the semaphore can be + * switched off: in this mode the {@link #acquire()} method never blocks, but + * lets all callers pass directly. + *

+ *

+ * When the {@code TimedSemaphore} is no more needed its {@link #shutdown()} + * method should be called. This causes the periodic task that monitors the time + * interval to be canceled. If the {@code ScheduledExecutorService} has been + * created by the semaphore at construction time, it is also shut down. + * resources. After that {@link #acquire()} must not be called any more. + *

+ * + * @since 3.0 + * @version $Id: TimedSemaphore.java 1309977 2012-04-05 17:53:39Z ggregory $ + */ +public class TimedSemaphore { + /** + * Constant for a value representing no limit. If the limit is set to a + * value less or equal this constant, the {@code TimedSemaphore} will be + * effectively switched off. + */ + public static final int NO_LIMIT = 0; + + /** Constant for the thread pool size for the executor. */ + private static final int THREAD_POOL_SIZE = 1; + + /** The executor service for managing the timer thread. */ + private final ScheduledExecutorService executorService; + + /** Stores the period for this timed semaphore. */ + private final long period; + + /** The time unit for the period. */ + private final TimeUnit unit; + + /** A flag whether the executor service was created by this object. */ + private final boolean ownExecutor; + + /** A future object representing the timer task. */ + private ScheduledFuture task; + + /** Stores the total number of invocations of the acquire() method. */ + private long totalAcquireCount; + + /** + * The counter for the periods. This counter is increased every time a + * period ends. + */ + private long periodCount; + + /** The limit. */ + private int limit; + + /** The current counter. */ + private int acquireCount; + + /** The number of invocations of acquire() in the last period. */ + private int lastCallsPerPeriod; + + /** A flag whether shutdown() was called. */ + private boolean shutdown; + + /** + * Creates a new instance of {@link TimedSemaphore} and initializes it with + * the given time period and the limit. + * + * @param timePeriod the time period + * @param timeUnit the unit for the period + * @param limit the limit for the semaphore + * @throws IllegalArgumentException if the period is less or equals 0 + */ + public TimedSemaphore(long timePeriod, TimeUnit timeUnit, int limit) { + this(null, timePeriod, timeUnit, limit); + } + + /** + * Creates a new instance of {@link TimedSemaphore} and initializes it with + * an executor service, the given time period, and the limit. The executor + * service will be used for creating a periodic task for monitoring the time + * period. It can be null, then a default service will be created. + * + * @param service the executor service + * @param timePeriod the time period + * @param timeUnit the unit for the period + * @param limit the limit for the semaphore + * @throws IllegalArgumentException if the period is less or equals 0 + */ + public TimedSemaphore(ScheduledExecutorService service, long timePeriod, + TimeUnit timeUnit, int limit) { + if (timePeriod <= 0) { + throw new IllegalArgumentException("Time period must be greater 0!"); + } + + period = timePeriod; + unit = timeUnit; + + if (service != null) { + executorService = service; + ownExecutor = false; + } else { + ScheduledThreadPoolExecutor s = new ScheduledThreadPoolExecutor( + THREAD_POOL_SIZE); + s.setContinueExistingPeriodicTasksAfterShutdownPolicy(false); + s.setExecuteExistingDelayedTasksAfterShutdownPolicy(false); + executorService = s; + ownExecutor = true; + } + + setLimit(limit); + } + + /** + * Returns the limit enforced by this semaphore. The limit determines how + * many invocations of {@link #acquire()} are allowed within the monitored + * period. + * + * @return the limit + */ + public final synchronized int getLimit() { + return limit; + } + + /** + * Sets the limit. This is the number of times the {@link #acquire()} method + * can be called within the time period specified. If this limit is reached, + * further invocations of {@link #acquire()} will block. Setting the limit + * to a value <= {@link #NO_LIMIT} will cause the limit to be disabled, + * i.e. an arbitrary number of{@link #acquire()} invocations is allowed in + * the time period. + * + * @param limit the limit + */ + public final synchronized void setLimit(int limit) { + this.limit = limit; + } + + /** + * Initializes a shutdown. After that the object cannot be used any more. + * This method can be invoked an arbitrary number of times. All invocations + * after the first one do not have any effect. + */ + public synchronized void shutdown() { + if (!shutdown) { + + if (ownExecutor) { + // if the executor was created by this instance, it has + // to be shutdown + getExecutorService().shutdownNow(); + } + if (task != null) { + task.cancel(false); + } + + shutdown = true; + } + } + + /** + * Tests whether the {@link #shutdown()} method has been called on this + * object. If this method returns true, this instance cannot be used + * any longer. + * + * @return a flag whether a shutdown has been performed + */ + public synchronized boolean isShutdown() { + return shutdown; + } + + /** + * Tries to acquire a permit from this semaphore. This method will block if + * the limit for the current period has already been reached. If + * {@link #shutdown()} has already been invoked, calling this method will + * cause an exception. The very first call of this method starts the timer + * task which monitors the time period set for this {@code TimedSemaphore}. + * From now on the semaphore is active. + * + * @throws InterruptedException if the thread gets interrupted + * @throws IllegalStateException if this semaphore is already shut down + */ + public synchronized void acquire() throws InterruptedException { + if (isShutdown()) { + throw new IllegalStateException("TimedSemaphore is shut down!"); + } + + if (task == null) { + task = startTimer(); + } + + boolean canPass = false; + do { + canPass = getLimit() <= NO_LIMIT || acquireCount < getLimit(); + if (!canPass) { + wait(); + } else { + acquireCount++; + } + } while (!canPass); + } + + /** + * Returns the number of (successful) acquire invocations during the last + * period. This is the number of times the {@link #acquire()} method was + * called without blocking. This can be useful for testing or debugging + * purposes or to determine a meaningful threshold value. If a limit is set, + * the value returned by this method won't be greater than this limit. + * + * @return the number of non-blocking invocations of the {@link #acquire()} + * method + */ + public synchronized int getLastAcquiresPerPeriod() { + return lastCallsPerPeriod; + } + + /** + * Returns the number of invocations of the {@link #acquire()} method for + * the current period. This may be useful for testing or debugging purposes. + * + * @return the current number of {@link #acquire()} invocations + */ + public synchronized int getAcquireCount() { + return acquireCount; + } + + /** + * Returns the number of calls to the {@link #acquire()} method that can + * still be performed in the current period without blocking. This method + * can give an indication whether it is safe to call the {@link #acquire()} + * method without risking to be suspended. However, there is no guarantee + * that a subsequent call to {@link #acquire()} actually is not-blocking + * because in the mean time other threads may have invoked the semaphore. + * + * @return the current number of available {@link #acquire()} calls in the + * current period + */ + public synchronized int getAvailablePermits() { + return getLimit() - getAcquireCount(); + } + + /** + * Returns the average number of successful (i.e. non-blocking) + * {@link #acquire()} invocations for the entire life-time of this {@code + * TimedSemaphore}. This method can be used for instance for statistical + * calculations. + * + * @return the average number of {@link #acquire()} invocations per time + * unit + */ + public synchronized double getAverageCallsPerPeriod() { + return periodCount == 0 ? 0 : (double) totalAcquireCount + / (double) periodCount; + } + + /** + * Returns the time period. This is the time monitored by this semaphore. + * Only a given number of invocations of the {@link #acquire()} method is + * possible in this period. + * + * @return the time period + */ + public long getPeriod() { + return period; + } + + /** + * Returns the time unit. This is the unit used by {@link #getPeriod()}. + * + * @return the time unit + */ + public TimeUnit getUnit() { + return unit; + } + + /** + * Returns the executor service used by this instance. + * + * @return the executor service + */ + protected ScheduledExecutorService getExecutorService() { + return executorService; + } + + /** + * Starts the timer. This method is called when {@link #acquire()} is called + * for the first time. It schedules a task to be executed at fixed rate to + * monitor the time period specified. + * + * @return a future object representing the task scheduled + */ + protected ScheduledFuture startTimer() { + return getExecutorService().scheduleAtFixedRate(new Runnable() { + @Override + public void run() { + endOfPeriod(); + } + }, getPeriod(), getPeriod(), getUnit()); + } + + /** + * The current time period is finished. This method is called by the timer + * used internally to monitor the time period. It resets the counter and + * releases the threads waiting for this barrier. + */ + synchronized void endOfPeriod() { + lastCallsPerPeriod = acquireCount; + totalAcquireCount += acquireCount; + periodCount++; + acquireCount = 0; + notifyAll(); + } +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/concurrent/TimedSemaphoreTest.java b/ApacheCommonsLang/org/apache/commons/lang3/concurrent/TimedSemaphoreTest.java new file mode 100644 index 0000000..d6b4475 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/concurrent/TimedSemaphoreTest.java @@ -0,0 +1,480 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.concurrent; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import org.easymock.EasyMock; +import org.junit.Test; + +/** + * Test class for TimedSemaphore. + * + * @version $Id: TimedSemaphoreTest.java 1199894 2011-11-09 17:53:59Z ggregory $ + */ +public class TimedSemaphoreTest { + /** Constant for the time period. */ + private static final long PERIOD = 500; + + /** Constant for the time unit. */ + private static final TimeUnit UNIT = TimeUnit.MILLISECONDS; + + /** Constant for the default limit. */ + private static final int LIMIT = 10; + + /** + * Tests creating a new instance. + */ + @Test + public void testInit() { + ScheduledExecutorService service = EasyMock + .createMock(ScheduledExecutorService.class); + EasyMock.replay(service); + TimedSemaphore semaphore = new TimedSemaphore(service, PERIOD, UNIT, + LIMIT); + EasyMock.verify(service); + assertEquals("Wrong service", service, semaphore.getExecutorService()); + assertEquals("Wrong period", PERIOD, semaphore.getPeriod()); + assertEquals("Wrong unit", UNIT, semaphore.getUnit()); + assertEquals("Statistic available", 0, semaphore + .getLastAcquiresPerPeriod()); + assertEquals("Average available", 0.0, semaphore + .getAverageCallsPerPeriod(), .05); + assertFalse("Already shutdown", semaphore.isShutdown()); + assertEquals("Wrong limit", LIMIT, semaphore.getLimit()); + } + + /** + * Tries to create an instance with a negative period. This should cause an + * exception. + */ + @Test(expected = IllegalArgumentException.class) + public void testInitInvalidPeriod() { + new TimedSemaphore(0L, UNIT, LIMIT); + } + + /** + * Tests whether a default executor service is created if no service is + * provided. + */ + @Test + public void testInitDefaultService() { + TimedSemaphore semaphore = new TimedSemaphore(PERIOD, UNIT, LIMIT); + ScheduledThreadPoolExecutor exec = (ScheduledThreadPoolExecutor) semaphore + .getExecutorService(); + assertFalse("Wrong periodic task policy", exec + .getContinueExistingPeriodicTasksAfterShutdownPolicy()); + assertFalse("Wrong delayed task policy", exec + .getExecuteExistingDelayedTasksAfterShutdownPolicy()); + assertFalse("Already shutdown", exec.isShutdown()); + semaphore.shutdown(); + } + + /** + * Tests starting the timer. + */ + @Test + public void testStartTimer() throws InterruptedException { + TimedSemaphoreTestImpl semaphore = new TimedSemaphoreTestImpl(PERIOD, + UNIT, LIMIT); + ScheduledFuture future = semaphore.startTimer(); + assertNotNull("No future returned", future); + Thread.sleep(PERIOD); + final int trials = 10; + int count = 0; + do { + Thread.sleep(PERIOD); + if (count++ > trials) { + fail("endOfPeriod() not called!"); + } + } while (semaphore.getPeriodEnds() <= 0); + semaphore.shutdown(); + } + + /** + * Tests the shutdown() method if the executor belongs to the semaphore. In + * this case it has to be shut down. + */ + @Test + public void testShutdownOwnExecutor() { + TimedSemaphore semaphore = new TimedSemaphore(PERIOD, UNIT, LIMIT); + semaphore.shutdown(); + assertTrue("Not shutdown", semaphore.isShutdown()); + assertTrue("Executor not shutdown", semaphore.getExecutorService() + .isShutdown()); + } + + /** + * Tests the shutdown() method for a shared executor service before a task + * was started. This should do pretty much nothing. + */ + @Test + public void testShutdownSharedExecutorNoTask() { + ScheduledExecutorService service = EasyMock + .createMock(ScheduledExecutorService.class); + EasyMock.replay(service); + TimedSemaphore semaphore = new TimedSemaphore(service, PERIOD, UNIT, + LIMIT); + semaphore.shutdown(); + assertTrue("Not shutdown", semaphore.isShutdown()); + EasyMock.verify(service); + } + + /** + * Prepares an executor service mock to expect the start of the timer. + * + * @param service the mock + * @param future the future + */ + private void prepareStartTimer(ScheduledExecutorService service, + ScheduledFuture future) { + service.scheduleAtFixedRate((Runnable) EasyMock.anyObject(), EasyMock + .eq(PERIOD), EasyMock.eq(PERIOD), EasyMock.eq(UNIT)); + EasyMock.expectLastCall().andReturn(future); + } + + /** + * Tests the shutdown() method for a shared executor after the task was + * started. In this case the task must be canceled. + */ + @Test + public void testShutdownSharedExecutorTask() throws InterruptedException { + ScheduledExecutorService service = EasyMock + .createMock(ScheduledExecutorService.class); + ScheduledFuture future = EasyMock.createMock(ScheduledFuture.class); + prepareStartTimer(service, future); + EasyMock.expect(Boolean.valueOf(future.cancel(false))).andReturn(Boolean.TRUE); + EasyMock.replay(service, future); + TimedSemaphoreTestImpl semaphore = new TimedSemaphoreTestImpl(service, + PERIOD, UNIT, LIMIT); + semaphore.acquire(); + semaphore.shutdown(); + assertTrue("Not shutdown", semaphore.isShutdown()); + EasyMock.verify(service, future); + } + + /** + * Tests multiple invocations of the shutdown() method. + */ + @Test + public void testShutdownMultipleTimes() throws InterruptedException { + ScheduledExecutorService service = EasyMock + .createMock(ScheduledExecutorService.class); + ScheduledFuture future = EasyMock.createMock(ScheduledFuture.class); + prepareStartTimer(service, future); + EasyMock.expect(Boolean.valueOf(future.cancel(false))).andReturn(Boolean.TRUE); + EasyMock.replay(service, future); + TimedSemaphoreTestImpl semaphore = new TimedSemaphoreTestImpl(service, + PERIOD, UNIT, LIMIT); + semaphore.acquire(); + for (int i = 0; i < 10; i++) { + semaphore.shutdown(); + } + EasyMock.verify(service, future); + } + + /** + * Tests the acquire() method if a limit is set. + */ + @Test + public void testAcquireLimit() throws InterruptedException { + ScheduledExecutorService service = EasyMock + .createMock(ScheduledExecutorService.class); + ScheduledFuture future = EasyMock.createMock(ScheduledFuture.class); + prepareStartTimer(service, future); + EasyMock.replay(service, future); + final int count = 10; + CountDownLatch latch = new CountDownLatch(count - 1); + TimedSemaphore semaphore = new TimedSemaphore(service, PERIOD, UNIT, 1); + SemaphoreThread t = new SemaphoreThread(semaphore, latch, count, + count - 1); + semaphore.setLimit(count - 1); + + // start a thread that calls the semaphore count times + t.start(); + latch.await(); + // now the semaphore's limit should be reached and the thread blocked + assertEquals("Wrong semaphore count", count - 1, semaphore + .getAcquireCount()); + + // this wakes up the thread, it should call the semaphore once more + semaphore.endOfPeriod(); + t.join(); + assertEquals("Wrong semaphore count (2)", 1, semaphore + .getAcquireCount()); + assertEquals("Wrong acquire() count", count - 1, semaphore + .getLastAcquiresPerPeriod()); + EasyMock.verify(service, future); + } + + /** + * Tests the acquire() method if more threads are involved than the limit. + * This method starts a number of threads that all invoke the semaphore. The + * semaphore's limit is set to 1, so in each period only a single thread can + * acquire the semaphore. + */ + @Test + public void testAcquireMultipleThreads() throws InterruptedException { + ScheduledExecutorService service = EasyMock + .createMock(ScheduledExecutorService.class); + ScheduledFuture future = EasyMock.createMock(ScheduledFuture.class); + prepareStartTimer(service, future); + EasyMock.replay(service, future); + TimedSemaphoreTestImpl semaphore = new TimedSemaphoreTestImpl(service, + PERIOD, UNIT, 1); + semaphore.latch = new CountDownLatch(1); + final int count = 10; + SemaphoreThread[] threads = new SemaphoreThread[count]; + for (int i = 0; i < count; i++) { + threads[i] = new SemaphoreThread(semaphore, null, 1, 0); + threads[i].start(); + } + for (int i = 0; i < count; i++) { + semaphore.latch.await(); + assertEquals("Wrong count", 1, semaphore.getAcquireCount()); + semaphore.latch = new CountDownLatch(1); + semaphore.endOfPeriod(); + assertEquals("Wrong acquire count", 1, semaphore + .getLastAcquiresPerPeriod()); + } + for (int i = 0; i < count; i++) { + threads[i].join(); + } + EasyMock.verify(service, future); + } + + /** + * Tests the acquire() method if no limit is set. A test thread is started + * that calls the semaphore a large number of times. Even if the semaphore's + * period does not end, the thread should never block. + */ + @Test + public void testAcquireNoLimit() throws InterruptedException { + ScheduledExecutorService service = EasyMock + .createMock(ScheduledExecutorService.class); + ScheduledFuture future = EasyMock.createMock(ScheduledFuture.class); + prepareStartTimer(service, future); + EasyMock.replay(service, future); + TimedSemaphoreTestImpl semaphore = new TimedSemaphoreTestImpl(service, + PERIOD, UNIT, TimedSemaphore.NO_LIMIT); + final int count = 1000; + CountDownLatch latch = new CountDownLatch(count); + SemaphoreThread t = new SemaphoreThread(semaphore, latch, count, count); + t.start(); + latch.await(); + EasyMock.verify(service, future); + } + + /** + * Tries to call acquire() after shutdown(). This should cause an exception. + */ + @Test(expected = IllegalStateException.class) + public void testPassAfterShutdown() throws InterruptedException { + TimedSemaphore semaphore = new TimedSemaphore(PERIOD, UNIT, LIMIT); + semaphore.shutdown(); + semaphore.acquire(); + } + + /** + * Tests a bigger number of invocations that span multiple periods. The + * period is set to a very short time. A background thread calls the + * semaphore a large number of times. While it runs at last one end of a + * period should be reached. + */ + @Test + public void testAcquireMultiplePeriods() throws InterruptedException { + final int count = 1000; + TimedSemaphoreTestImpl semaphore = new TimedSemaphoreTestImpl( + PERIOD / 10, TimeUnit.MILLISECONDS, 1); + semaphore.setLimit(count / 4); + CountDownLatch latch = new CountDownLatch(count); + SemaphoreThread t = new SemaphoreThread(semaphore, latch, count, count); + t.start(); + latch.await(); + semaphore.shutdown(); + assertTrue("End of period not reached", semaphore.getPeriodEnds() > 0); + } + + /** + * Tests the methods for statistics. + */ + @Test + public void testGetAverageCallsPerPeriod() throws InterruptedException { + ScheduledExecutorService service = EasyMock + .createMock(ScheduledExecutorService.class); + ScheduledFuture future = EasyMock.createMock(ScheduledFuture.class); + prepareStartTimer(service, future); + EasyMock.replay(service, future); + TimedSemaphore semaphore = new TimedSemaphore(service, PERIOD, UNIT, + LIMIT); + semaphore.acquire(); + semaphore.endOfPeriod(); + assertEquals("Wrong average (1)", 1.0, semaphore + .getAverageCallsPerPeriod(), .005); + semaphore.acquire(); + semaphore.acquire(); + semaphore.endOfPeriod(); + assertEquals("Wrong average (2)", 1.5, semaphore + .getAverageCallsPerPeriod(), .005); + EasyMock.verify(service, future); + } + + /** + * Tests whether the available non-blocking calls can be queried. + */ + @Test + public void testGetAvailablePermits() throws InterruptedException { + ScheduledExecutorService service = EasyMock + .createMock(ScheduledExecutorService.class); + ScheduledFuture future = EasyMock.createMock(ScheduledFuture.class); + prepareStartTimer(service, future); + EasyMock.replay(service, future); + TimedSemaphore semaphore = new TimedSemaphore(service, PERIOD, UNIT, + LIMIT); + for (int i = 0; i < LIMIT; i++) { + assertEquals("Wrong available count at " + i, LIMIT - i, semaphore + .getAvailablePermits()); + semaphore.acquire(); + } + semaphore.endOfPeriod(); + assertEquals("Wrong available count in new period", LIMIT, semaphore + .getAvailablePermits()); + EasyMock.verify(service, future); + } + + /** + * A specialized implementation of {@code TimedSemaphore} that is easier to + * test. + */ + private static class TimedSemaphoreTestImpl extends TimedSemaphore { + /** A mock scheduled future. */ + ScheduledFuture schedFuture; + + /** A latch for synchronizing with the main thread. */ + volatile CountDownLatch latch; + + /** Counter for the endOfPeriod() invocations. */ + private int periodEnds; + + public TimedSemaphoreTestImpl(long timePeriod, TimeUnit timeUnit, + int limit) { + super(timePeriod, timeUnit, limit); + } + + public TimedSemaphoreTestImpl(ScheduledExecutorService service, + long timePeriod, TimeUnit timeUnit, int limit) { + super(service, timePeriod, timeUnit, limit); + } + + /** + * Returns the number of invocations of the endOfPeriod() method. + * + * @return the endOfPeriod() invocations + */ + public int getPeriodEnds() { + synchronized (this) { + return periodEnds; + } + } + + /** + * Invokes the latch if one is set. + */ + @Override + public void acquire() throws InterruptedException { + super.acquire(); + if (latch != null) { + latch.countDown(); + } + } + + /** + * Counts the number of invocations. + */ + @Override + protected void endOfPeriod() { + super.endOfPeriod(); + synchronized (this) { + periodEnds++; + } + } + + /** + * Either returns the mock future or calls the super method. + */ + @Override + protected ScheduledFuture startTimer() { + return schedFuture != null ? schedFuture : super.startTimer(); + } + } + + /** + * A test thread class that will be used by tests for triggering the + * semaphore. The thread calls the semaphore a configurable number of times. + * When this is done, it can notify the main thread. + */ + private static class SemaphoreThread extends Thread { + /** The semaphore. */ + private final TimedSemaphore semaphore; + + /** A latch for communication with the main thread. */ + private final CountDownLatch latch; + + /** The number of acquire() calls. */ + private final int count; + + /** The number of invocations of the latch. */ + private final int latchCount; + + public SemaphoreThread(TimedSemaphore b, CountDownLatch l, int c, int lc) { + semaphore = b; + latch = l; + count = c; + latchCount = lc; + } + + /** + * Calls acquire() on the semaphore for the specified number of times. + * Optionally the latch will also be triggered to synchronize with the + * main test thread. + */ + @Override + public void run() { + try { + for (int i = 0; i < count; i++) { + semaphore.acquire(); + + if (i < latchCount) { + latch.countDown(); + } + } + } catch (InterruptedException iex) { + Thread.currentThread().interrupt(); + } + } + } +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/concurrent/package.html b/ApacheCommonsLang/org/apache/commons/lang3/concurrent/package.html new file mode 100644 index 0000000..cd4cd29 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/concurrent/package.html @@ -0,0 +1,23 @@ + + + +Provides support classes for multi-threaded programming. This package is +intended to be an extension to java.util.concurrent. +

These classes are thread-safe.

+ + diff --git a/ApacheCommonsLang/org/apache/commons/lang3/event/EventListenerSupport.java b/ApacheCommonsLang/org/apache/commons/lang3/event/EventListenerSupport.java new file mode 100644 index 0000000..c5ef61d --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/event/EventListenerSupport.java @@ -0,0 +1,315 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.lang3.event; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.lang.reflect.Array; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +import org.apache.commons.lang3.Validate; + +/** + * An EventListenerSupport object can be used to manage a list of event + * listeners of a particular type. The class provides + * {@link #addListener(Object)} and {@link #removeListener(Object)} methods + * for registering listeners, as well as a {@link #fire()} method for firing + * events to the listeners. + * + *

+ * To use this class, suppose you want to support ActionEvents. You would do: + *

+ * public class MyActionEventSource
+ * {
+ *   private EventListenerSupport actionListeners =
+ *       EventListenerSupport.create(ActionListener.class);
+ *
+ *   public void someMethodThatFiresAction()
+ *   {
+ *     ActionEvent e = new ActionEvent(this, ActionEvent.ACTION_PERFORMED, "somethingCool");
+ *     actionListeners.fire().actionPerformed(e);
+ *   }
+ * }
+ * 
+ * + * Serializing an {@link EventListenerSupport} instance will result in any + * non-{@link Serializable} listeners being silently dropped. + * + * @param the type of event listener that is supported by this proxy. + * + * @since 3.0 + * @version $Id: EventListenerSupport.java 1309977 2012-04-05 17:53:39Z ggregory $ + */ +public class EventListenerSupport implements Serializable { + + /** Serialization version */ + private static final long serialVersionUID = 3593265990380473632L; + + /** + * The list used to hold the registered listeners. This list is + * intentionally a thread-safe copy-on-write-array so that traversals over + * the list of listeners will be atomic. + */ + private List listeners = new CopyOnWriteArrayList(); + + /** + * The proxy representing the collection of listeners. Calls to this proxy + * object will sent to all registered listeners. + */ + private transient L proxy; + + /** + * Empty typed array for #getListeners(). + */ + private transient L[] prototypeArray; + + /** + * Creates an EventListenerSupport object which supports the specified + * listener type. + * + * @param the type of the listener interface + * @param listenerInterface the type of listener interface that will receive + * events posted using this class. + * + * @return an EventListenerSupport object which supports the specified + * listener type. + * + * @throws NullPointerException if listenerInterface is + * null. + * @throws IllegalArgumentException if listenerInterface is + * not an interface. + */ + public static EventListenerSupport create(Class listenerInterface) { + return new EventListenerSupport(listenerInterface); + } + + /** + * Creates an EventListenerSupport object which supports the provided + * listener interface. + * + * @param listenerInterface the type of listener interface that will receive + * events posted using this class. + * + * @throws NullPointerException if listenerInterface is + * null. + * @throws IllegalArgumentException if listenerInterface is + * not an interface. + */ + public EventListenerSupport(Class listenerInterface) { + this(listenerInterface, Thread.currentThread().getContextClassLoader()); + } + + /** + * Creates an EventListenerSupport object which supports the provided + * listener interface using the specified class loader to create the JDK + * dynamic proxy. + * + * @param listenerInterface the listener interface. + * @param classLoader the class loader. + * + * @throws NullPointerException if listenerInterface or + * classLoader is null. + * @throws IllegalArgumentException if listenerInterface is + * not an interface. + */ + public EventListenerSupport(Class listenerInterface, ClassLoader classLoader) { + this(); + Validate.notNull(listenerInterface, "Listener interface cannot be null."); + Validate.notNull(classLoader, "ClassLoader cannot be null."); + Validate.isTrue(listenerInterface.isInterface(), "Class {0} is not an interface", + listenerInterface.getName()); + initializeTransientFields(listenerInterface, classLoader); + } + + /** + * Create a new EventListenerSupport instance. + * Serialization-friendly constructor. + */ + private EventListenerSupport() { + } + + /** + * Returns a proxy object which can be used to call listener methods on all + * of the registered event listeners. All calls made to this proxy will be + * forwarded to all registered listeners. + * + * @return a proxy object which can be used to call listener methods on all + * of the registered event listeners + */ + public L fire() { + return proxy; + } + +//********************************************************************************************************************** +// Other Methods +//********************************************************************************************************************** + + /** + * Registers an event listener. + * + * @param listener the event listener (may not be null). + * + * @throws NullPointerException if listener is + * null. + */ + public void addListener(L listener) { + Validate.notNull(listener, "Listener object cannot be null."); + listeners.add(listener); + } + + /** + * Returns the number of registered listeners. + * + * @return the number of registered listeners. + */ + int getListenerCount() { + return listeners.size(); + } + + /** + * Unregisters an event listener. + * + * @param listener the event listener (may not be null). + * + * @throws NullPointerException if listener is + * null. + */ + public void removeListener(L listener) { + Validate.notNull(listener, "Listener object cannot be null."); + listeners.remove(listener); + } + + /** + * Get an array containing the currently registered listeners. + * Modification to this array's elements will have no effect on the + * {@link EventListenerSupport} instance. + * @return L[] + */ + public L[] getListeners() { + return listeners.toArray(prototypeArray); + } + + /** + * Serialize. + * @param objectOutputStream the output stream + * @throws IOException if an IO error occurs + */ + private void writeObject(ObjectOutputStream objectOutputStream) throws IOException { + ArrayList serializableListeners = new ArrayList(); + + // don't just rely on instanceof Serializable: + ObjectOutputStream testObjectOutputStream = new ObjectOutputStream(new ByteArrayOutputStream()); + for (L listener : listeners) { + try { + testObjectOutputStream.writeObject(listener); + serializableListeners.add(listener); + } catch (IOException exception) { + //recreate test stream in case of indeterminate state + testObjectOutputStream = new ObjectOutputStream(new ByteArrayOutputStream()); + } + } + /* + * we can reconstitute everything we need from an array of our listeners, + * which has the additional advantage of typically requiring less storage than a list: + */ + objectOutputStream.writeObject(serializableListeners.toArray(prototypeArray)); + } + + /** + * Deserialize. + * @param objectInputStream the input stream + * @throws IOException if an IO error occurs + * @throws ClassNotFoundException if the class cannot be resolved + */ + private void readObject(ObjectInputStream objectInputStream) throws IOException, ClassNotFoundException { + @SuppressWarnings("unchecked") + L[] listeners = (L[]) objectInputStream.readObject(); + + this.listeners = new CopyOnWriteArrayList(listeners); + + @SuppressWarnings("unchecked") + Class listenerInterface = (Class) listeners.getClass().getComponentType(); + + initializeTransientFields(listenerInterface, Thread.currentThread().getContextClassLoader()); + } + + /** + * Initialize transient fields. + * @param listenerInterface the class of the listener interface + * @param classLoader the class loader to be used + */ + private void initializeTransientFields(Class listenerInterface, ClassLoader classLoader) { + @SuppressWarnings("unchecked") + L[] array = (L[]) Array.newInstance(listenerInterface, 0); + this.prototypeArray = array; + createProxy(listenerInterface, classLoader); + } + + /** + * Create the proxy object. + * @param listenerInterface the class of the listener interface + * @param classLoader the class loader to be used + */ + private void createProxy(Class listenerInterface, ClassLoader classLoader) { + proxy = listenerInterface.cast(Proxy.newProxyInstance(classLoader, + new Class[] { listenerInterface }, createInvocationHandler())); + } + + /** + * Create the {@link InvocationHandler} responsible for broadcasting calls + * to the managed listeners. Subclasses can override to provide custom behavior. + * @return ProxyInvocationHandler + */ + protected InvocationHandler createInvocationHandler() { + return new ProxyInvocationHandler(); + } + + /** + * An invocation handler used to dispatch the event(s) to all the listeners. + */ + protected class ProxyInvocationHandler implements InvocationHandler { + + /** + * Propagates the method call to all registered listeners in place of + * the proxy listener object. + * + * @param proxy the proxy object representing a listener on which the + * invocation was called. + * @param method the listener method that will be called on all of the + * listeners. + * @param args event arguments to propagate to the listeners. + * @return the result of the method call + * @throws Throwable if an error occurs + */ + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + for (L listener : listeners) { + method.invoke(listener, args); + } + return null; + } + } +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/event/EventListenerSupportTest.java b/ApacheCommonsLang/org/apache/commons/lang3/event/EventListenerSupportTest.java new file mode 100644 index 0000000..f333b13 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/event/EventListenerSupportTest.java @@ -0,0 +1,243 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.lang3.event; + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyVetoException; +import java.beans.VetoableChangeListener; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import junit.framework.TestCase; + +import org.easymock.EasyMock; + +/** + * @since 3.0 + * @version $Id: EventListenerSupportTest.java 1309977 2012-04-05 17:53:39Z ggregory $ + */ +public class EventListenerSupportTest extends TestCase +{ + public void testAddNullListener() + { + EventListenerSupport listenerSupport = EventListenerSupport.create(VetoableChangeListener.class); + try + { + listenerSupport.addListener(null); + fail("Should not be able to add a null listener."); + } + catch (NullPointerException e) + { + + } + } + + public void testRemoveNullListener() + { + EventListenerSupport listenerSupport = EventListenerSupport.create(VetoableChangeListener.class); + try + { + listenerSupport.removeListener(null); + fail("Should not be able to remove a null listener."); + } + catch (NullPointerException e) + { + + } + } + + public void testEventDispatchOrder() throws PropertyVetoException + { + EventListenerSupport listenerSupport = EventListenerSupport.create(VetoableChangeListener.class); + final List calledListeners = new ArrayList(); + + final VetoableChangeListener listener1 = createListener(calledListeners); + final VetoableChangeListener listener2 = createListener(calledListeners); + listenerSupport.addListener(listener1); + listenerSupport.addListener(listener2); + listenerSupport.fire().vetoableChange(new PropertyChangeEvent(new Date(), "Day", 4, 5)); + assertEquals(calledListeners.size(), 2); + assertSame(calledListeners.get(0), listener1); + assertSame(calledListeners.get(1), listener2); + } + + public void testCreateWithNonInterfaceParameter() + { + try + { + EventListenerSupport.create(String.class); + fail("Should not be able to create using non-interface class."); + } + catch (IllegalArgumentException e) + { + + } + } + + public void testCreateWithNullParameter() + { + try + { + EventListenerSupport.create(null); + fail("Should not be able to create using null class."); + } + catch (NullPointerException e) + { + + } + } + + public void testRemoveListenerDuringEvent() throws PropertyVetoException + { + final EventListenerSupport listenerSupport = EventListenerSupport.create(VetoableChangeListener.class); + for (int i = 0; i < 10; ++i) + { + addDeregisterListener(listenerSupport); + } + assertEquals(listenerSupport.getListenerCount(), 10); + listenerSupport.fire().vetoableChange(new PropertyChangeEvent(new Date(), "Day", 4, 5)); + assertEquals(listenerSupport.getListenerCount(), 0); + } + + public void testGetListeners() { + final EventListenerSupport listenerSupport = EventListenerSupport.create(VetoableChangeListener.class); + + VetoableChangeListener[] listeners = listenerSupport.getListeners(); + assertEquals(0, listeners.length); + assertEquals(VetoableChangeListener.class, listeners.getClass().getComponentType()); + VetoableChangeListener[] empty = listeners; + //for fun, show that the same empty instance is used + assertSame(empty, listenerSupport.getListeners()); + + VetoableChangeListener listener1 = EasyMock.createNiceMock(VetoableChangeListener.class); + listenerSupport.addListener(listener1); + assertEquals(1, listenerSupport.getListeners().length); + VetoableChangeListener listener2 = EasyMock.createNiceMock(VetoableChangeListener.class); + listenerSupport.addListener(listener2); + assertEquals(2, listenerSupport.getListeners().length); + listenerSupport.removeListener(listener1); + assertEquals(1, listenerSupport.getListeners().length); + listenerSupport.removeListener(listener2); + assertSame(empty, listenerSupport.getListeners()); + } + + public void testSerialization() throws IOException, ClassNotFoundException, PropertyVetoException { + EventListenerSupport listenerSupport = EventListenerSupport.create(VetoableChangeListener.class); + listenerSupport.addListener(new VetoableChangeListener() { + + @Override + public void vetoableChange(PropertyChangeEvent e) { + } + }); + listenerSupport.addListener(EasyMock.createNiceMock(VetoableChangeListener.class)); + + //serialize: + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream); + + objectOutputStream.writeObject(listenerSupport); + objectOutputStream.close(); + + //deserialize: + @SuppressWarnings("unchecked") + EventListenerSupport deserializedListenerSupport = (EventListenerSupport) new ObjectInputStream( + new ByteArrayInputStream(outputStream.toByteArray())).readObject(); + + //make sure we get a listener array back, of the correct component type, and that it contains only the serializable mock + VetoableChangeListener[] listeners = deserializedListenerSupport.getListeners(); + assertEquals(VetoableChangeListener.class, listeners.getClass().getComponentType()); + assertEquals(1, listeners.length); + + //now verify that the mock still receives events; we can infer that the proxy was correctly reconstituted + VetoableChangeListener listener = listeners[0]; + PropertyChangeEvent evt = new PropertyChangeEvent(new Date(), "Day", 7, 9); + listener.vetoableChange(evt); + EasyMock.replay(listener); + deserializedListenerSupport.fire().vetoableChange(evt); + EasyMock.verify(listener); + + //remove listener and verify we get an empty array of listeners + deserializedListenerSupport.removeListener(listener); + assertEquals(0, deserializedListenerSupport.getListeners().length); + } + + public void testSubclassInvocationHandling() throws PropertyVetoException { + + @SuppressWarnings("serial") + EventListenerSupport eventListenerSupport = new EventListenerSupport( + VetoableChangeListener.class) { + @Override + protected java.lang.reflect.InvocationHandler createInvocationHandler() { + return new ProxyInvocationHandler() { + /** + * {@inheritDoc} + */ + @Override + public Object invoke(Object proxy, Method method, Object[] args) + throws Throwable { + return "vetoableChange".equals(method.getName()) + && "Hour".equals(((PropertyChangeEvent) args[0]).getPropertyName()) ? null + : super.invoke(proxy, method, args); + } + }; + } + }; + + VetoableChangeListener listener = EasyMock.createNiceMock(VetoableChangeListener.class); + eventListenerSupport.addListener(listener); + Object source = new Date(); + PropertyChangeEvent ignore = new PropertyChangeEvent(source, "Hour", 5, 6); + PropertyChangeEvent respond = new PropertyChangeEvent(source, "Day", 6, 7); + listener.vetoableChange(respond); + EasyMock.replay(listener); + eventListenerSupport.fire().vetoableChange(ignore); + eventListenerSupport.fire().vetoableChange(respond); + EasyMock.verify(listener); + } + + private void addDeregisterListener(final EventListenerSupport listenerSupport) + { + listenerSupport.addListener(new VetoableChangeListener() + { + @Override + public void vetoableChange(PropertyChangeEvent e) + { + listenerSupport.removeListener(this); + } + }); + } + + private VetoableChangeListener createListener(final List calledListeners) + { + return new VetoableChangeListener() + { + @Override + public void vetoableChange(PropertyChangeEvent e) + { + calledListeners.add(this); + } + }; + } +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/event/EventUtils.java b/ApacheCommonsLang/org/apache/commons/lang3/event/EventUtils.java new file mode 100644 index 0000000..1ce6f42 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/event/EventUtils.java @@ -0,0 +1,131 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.lang3.event; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import org.apache.commons.lang3.reflect.MethodUtils; + +/** + * Provides some useful event-based utility methods. + * + * @since 3.0 + * @version $Id: EventUtils.java 1309977 2012-04-05 17:53:39Z ggregory $ + */ +public class EventUtils { + + /** + * Adds an event listener to the specified source. This looks for an "add" method corresponding to the event + * type (addActionListener, for example). + * @param eventSource the event source + * @param listenerType the event listener type + * @param listener the listener + * @param the event listener type + * + * @throws IllegalArgumentException if the object doesn't support the listener type + */ + public static void addEventListener(Object eventSource, Class listenerType, L listener) { + try { + MethodUtils.invokeMethod(eventSource, "add" + listenerType.getSimpleName(), listener); + } catch (NoSuchMethodException e) { + throw new IllegalArgumentException("Class " + eventSource.getClass().getName() + + " does not have a public add" + listenerType.getSimpleName() + + " method which takes a parameter of type " + listenerType.getName() + "."); + } catch (IllegalAccessException e) { + throw new IllegalArgumentException("Class " + eventSource.getClass().getName() + + " does not have an accessible add" + listenerType.getSimpleName () + + " method which takes a parameter of type " + listenerType.getName() + "."); + } catch (InvocationTargetException e) { + throw new RuntimeException("Unable to add listener.", e.getCause()); + } + } + + /** + * Binds an event listener to a specific method on a specific object. + * + * @param the event listener type + * @param target the target object + * @param methodName the name of the method to be called + * @param eventSource the object which is generating events (JButton, JList, etc.) + * @param listenerType the listener interface (ActionListener.class, SelectionListener.class, etc.) + * @param eventTypes the event types (method names) from the listener interface (if none specified, all will be + * supported) + */ + public static void bindEventsToMethod(Object target, String methodName, Object eventSource, + Class listenerType, String... eventTypes) { + final L listener = listenerType.cast(Proxy.newProxyInstance(target.getClass().getClassLoader(), + new Class[] { listenerType }, new EventBindingInvocationHandler(target, methodName, eventTypes))); + addEventListener(eventSource, listenerType, listener); + } + + private static class EventBindingInvocationHandler implements InvocationHandler { + private final Object target; + private final String methodName; + private final Set eventTypes; + + /** + * Creates a new instance of {@code EventBindingInvocationHandler}. + * + * @param target the target object for method invocations + * @param methodName the name of the method to be invoked + * @param eventTypes the names of the supported event types + */ + EventBindingInvocationHandler(final Object target, final String methodName, String[] eventTypes) { + this.target = target; + this.methodName = methodName; + this.eventTypes = new HashSet(Arrays.asList(eventTypes)); + } + + /** + * Handles a method invocation on the proxy object. + * + * @param proxy the proxy instance + * @param method the method to be invoked + * @param parameters the parameters for the method invocation + * @return the result of the method call + * @throws Throwable if an error occurs + */ + @Override + public Object invoke(final Object proxy, final Method method, final Object[] parameters) throws Throwable { + if (eventTypes.isEmpty() || eventTypes.contains(method.getName())) { + if (hasMatchingParametersMethod(method)) { + return MethodUtils.invokeMethod(target, methodName, parameters); + } else { + return MethodUtils.invokeMethod(target, methodName); + } + } + return null; + } + + /** + * Checks whether a method for the passed in parameters can be found. + * + * @param method the listener method invoked + * @return a flag whether the parameters could be matched + */ + private boolean hasMatchingParametersMethod(final Method method) { + return MethodUtils.getAccessibleMethod(target.getClass(), methodName, method.getParameterTypes()) != null; + } + } +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/event/EventUtilsTest.java b/ApacheCommonsLang/org/apache/commons/lang3/event/EventUtilsTest.java new file mode 100644 index 0000000..46f235f --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/event/EventUtilsTest.java @@ -0,0 +1,267 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.event; + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.beans.VetoableChangeListener; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.Proxy; +import java.util.Date; +import java.util.Map; +import java.util.TreeMap; + +import javax.naming.event.ObjectChangeListener; + +import junit.framework.TestCase; + +/** + * @since 3.0 + * @version $Id: EventUtilsTest.java 1309977 2012-04-05 17:53:39Z ggregory $ + */ +public class EventUtilsTest extends TestCase +{ + + public void testConstructor() { + assertNotNull(new EventUtils()); + Constructor[] cons = EventUtils.class.getDeclaredConstructors(); + assertEquals(1, cons.length); + assertEquals(true, Modifier.isPublic(cons[0].getModifiers())); + assertEquals(true, Modifier.isPublic(EventUtils.class.getModifiers())); + assertEquals(false, Modifier.isFinal(EventUtils.class.getModifiers())); + } + + public void testAddEventListener() + { + final PropertyChangeSource src = new PropertyChangeSource(); + EventCountingInvociationHandler handler = new EventCountingInvociationHandler(); + PropertyChangeListener listener = handler.createListener(PropertyChangeListener.class); + assertEquals(0, handler.getEventCount("propertyChange")); + EventUtils.addEventListener(src, PropertyChangeListener.class, listener); + assertEquals(0, handler.getEventCount("propertyChange")); + src.setProperty("newValue"); + assertEquals(1, handler.getEventCount("propertyChange")); + } + + public void testAddEventListenerWithNoAddMethod() + { + final PropertyChangeSource src = new PropertyChangeSource(); + EventCountingInvociationHandler handler = new EventCountingInvociationHandler(); + ObjectChangeListener listener = handler.createListener(ObjectChangeListener.class); + try + { + EventUtils.addEventListener(src, ObjectChangeListener.class, listener); + fail("Should not be allowed to add a listener to an object that doesn't support it."); + } + catch (IllegalArgumentException e) + { + assertEquals("Class " + src.getClass().getName() + " does not have a public add" + ObjectChangeListener.class.getSimpleName() + " method which takes a parameter of type " + ObjectChangeListener.class.getName() + ".", e.getMessage()); + } + } + + public void testAddEventListenerThrowsException() + { + final ExceptionEventSource src = new ExceptionEventSource(); + try + { + EventUtils.addEventListener(src, PropertyChangeListener.class, new PropertyChangeListener() + { + @Override + public void propertyChange(PropertyChangeEvent e) + { + // Do nothing! + } + }); + fail("Add method should have thrown an exception, so method should fail."); + } + catch (RuntimeException e) + { + + } + } + + public void testAddEventListenerWithPrivateAddMethod() + { + final PropertyChangeSource src = new PropertyChangeSource(); + EventCountingInvociationHandler handler = new EventCountingInvociationHandler(); + VetoableChangeListener listener = handler.createListener(VetoableChangeListener.class); + try + { + EventUtils.addEventListener(src, VetoableChangeListener.class, listener); + fail("Should not be allowed to add a listener to an object that doesn't support it."); + } + catch (IllegalArgumentException e) + { + assertEquals("Class " + src.getClass().getName() + " does not have a public add" + VetoableChangeListener.class.getSimpleName() + " method which takes a parameter of type " + VetoableChangeListener.class.getName() + ".", e.getMessage()); + } + } + + public void testBindEventsToMethod() + { + final PropertyChangeSource src = new PropertyChangeSource(); + final EventCounter counter = new EventCounter(); + EventUtils.bindEventsToMethod(counter, "eventOccurred", src, PropertyChangeListener.class); + assertEquals(0, counter.getCount()); + src.setProperty("newValue"); + assertEquals(1, counter.getCount()); + } + + + public void testBindEventsToMethodWithEvent() + { + final PropertyChangeSource src = new PropertyChangeSource(); + final EventCounterWithEvent counter = new EventCounterWithEvent(); + EventUtils.bindEventsToMethod(counter, "eventOccurred", src, PropertyChangeListener.class); + assertEquals(0, counter.getCount()); + src.setProperty("newValue"); + assertEquals(1, counter.getCount()); + } + + + public void testBindFilteredEventsToMethod() + { + final MultipleEventSource src = new MultipleEventSource(); + final EventCounter counter = new EventCounter(); + EventUtils.bindEventsToMethod(counter, "eventOccurred", src, MultipleEventListener.class, "event1"); + assertEquals(0, counter.getCount()); + src.listeners.fire().event1(new PropertyChangeEvent(new Date(), "Day", Integer.valueOf(0), Integer.valueOf(1))); + assertEquals(1, counter.getCount()); + src.listeners.fire().event2(new PropertyChangeEvent(new Date(), "Day", Integer.valueOf(1), Integer.valueOf(2))); + assertEquals(1, counter.getCount()); + } + + public static interface MultipleEventListener + { + public void event1(PropertyChangeEvent e); + + public void event2(PropertyChangeEvent e); + } + + public static class EventCounter + { + private int count; + + public void eventOccurred() + { + count++; + } + + public int getCount() + { + return count; + } + } + + public static class EventCounterWithEvent + { + private int count; + + public void eventOccurred(PropertyChangeEvent e) + { + count++; + } + + public int getCount() + { + return count; + } + } + + + private static class EventCountingInvociationHandler implements InvocationHandler + { + private Map eventCounts = new TreeMap(); + + public L createListener(Class listenerType) + { + return listenerType.cast(Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), + new Class[]{listenerType}, + this)); + } + + public int getEventCount(String eventName) + { + Integer count = eventCounts.get(eventName); + return count == null ? 0 : count.intValue(); + } + + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable + { + Integer count = eventCounts.get(method.getName()); + if (count == null) + { + eventCounts.put(method.getName(), Integer.valueOf(1)); + } + else + { + eventCounts.put(method.getName(), Integer.valueOf(count.intValue() + 1)); + } + return null; + } + } + + public static class MultipleEventSource + { + private EventListenerSupport listeners = EventListenerSupport.create(MultipleEventListener.class); + + public void addMultipleEventListener(MultipleEventListener listener) + { + listeners.addListener(listener); + } + } + + public static class ExceptionEventSource + { + public void addPropertyChangeListener(PropertyChangeListener listener) + { + throw new RuntimeException(); + } + } + + public static class PropertyChangeSource + { + private EventListenerSupport listeners = EventListenerSupport.create(PropertyChangeListener.class); + + private String property; + + public void setProperty(String property) + { + String oldValue = this.property; + this.property = property; + listeners.fire().propertyChange(new PropertyChangeEvent(this, "property", oldValue, property)); + } + + protected void addVetoableChangeListener(VetoableChangeListener listener) + { + // Do nothing! + } + + public void addPropertyChangeListener(PropertyChangeListener listener) + { + listeners.addListener(listener); + } + + public void removePropertyChangeListener(PropertyChangeListener listener) + { + listeners.removeListener(listener); + } + } +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/event/package.html b/ApacheCommonsLang/org/apache/commons/lang3/event/package.html new file mode 100644 index 0000000..65ee1ad --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/event/package.html @@ -0,0 +1,22 @@ + + + +Provides some useful event-based utilities. +@since 3.0 + + diff --git a/ApacheCommonsLang/org/apache/commons/lang3/exception/AbstractExceptionContextTest.java b/ApacheCommonsLang/org/apache/commons/lang3/exception/AbstractExceptionContextTest.java new file mode 100644 index 0000000..ec57a2b --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/exception/AbstractExceptionContextTest.java @@ -0,0 +1,176 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.exception; + +import java.io.Serializable; +import java.util.Arrays; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.Set; + +import junit.framework.TestCase; + +import org.apache.commons.lang3.SerializationUtils; +import org.apache.commons.lang3.tuple.Pair; + + +/** + * Abstract test of an ExceptionContext implementation. + */ +public abstract class AbstractExceptionContextTest extends TestCase { + + protected static final String TEST_MESSAGE_2 = "This is monotonous"; + protected static final String TEST_MESSAGE = "Test Message"; + protected T exceptionContext; + + protected static class ObjectWithFaultyToString { + @Override + public String toString() { + throw new RuntimeException("Crap"); + } + } + + @Override + protected void setUp() throws Exception { + exceptionContext + .addContextValue("test1", null) + .addContextValue("test2", "some value") + .addContextValue("test Date", new Date()) + .addContextValue("test Nbr", Integer.valueOf(5)) + .addContextValue("test Poorly written obj", new ObjectWithFaultyToString()); + } + + public void testAddContextValue() { + String message = exceptionContext.getFormattedExceptionMessage(TEST_MESSAGE); + assertTrue(message.indexOf(TEST_MESSAGE) >= 0); + assertTrue(message.indexOf("test1") >= 0); + assertTrue(message.indexOf("test2") >= 0); + assertTrue(message.indexOf("test Date") >= 0); + assertTrue(message.indexOf("test Nbr") >= 0); + assertTrue(message.indexOf("some value") >= 0); + assertTrue(message.indexOf("5") >= 0); + + assertTrue(exceptionContext.getFirstContextValue("test1") == null); + assertTrue(exceptionContext.getFirstContextValue("test2").equals("some value")); + + assertEquals(5, exceptionContext.getContextLabels().size()); + assertTrue(exceptionContext.getContextLabels().contains("test1")); + assertTrue(exceptionContext.getContextLabels().contains("test2")); + assertTrue(exceptionContext.getContextLabels().contains("test Date")); + assertTrue(exceptionContext.getContextLabels().contains("test Nbr")); + + exceptionContext.addContextValue("test2", "different value"); + assertEquals(5, exceptionContext.getContextLabels().size()); + assertTrue(exceptionContext.getContextLabels().contains("test2")); + + String contextMessage = exceptionContext.getFormattedExceptionMessage(null); + assertTrue(contextMessage.indexOf(TEST_MESSAGE) == -1); + } + + public void testSetContextValue() { + exceptionContext.addContextValue("test2", "different value"); + exceptionContext.setContextValue("test3", "3"); + + String message = exceptionContext.getFormattedExceptionMessage(TEST_MESSAGE); + assertTrue(message.indexOf(TEST_MESSAGE) >= 0); + assertTrue(message.indexOf("test Poorly written obj") >= 0); + assertTrue(message.indexOf("Crap") >= 0); + + assertTrue(exceptionContext.getFirstContextValue("crap") == null); + assertTrue(exceptionContext.getFirstContextValue("test Poorly written obj") instanceof ObjectWithFaultyToString); + + assertEquals(7, exceptionContext.getContextEntries().size()); + assertEquals(6, exceptionContext.getContextLabels().size()); + + assertTrue(exceptionContext.getContextLabels().contains("test Poorly written obj")); + assertTrue(!exceptionContext.getContextLabels().contains("crap")); + + exceptionContext.setContextValue("test Poorly written obj", "replacement"); + + assertEquals(7, exceptionContext.getContextEntries().size()); + assertEquals(6, exceptionContext.getContextLabels().size()); + + exceptionContext.setContextValue("test2", "another"); + + assertEquals(6, exceptionContext.getContextEntries().size()); + assertEquals(6, exceptionContext.getContextLabels().size()); + + String contextMessage = exceptionContext.getFormattedExceptionMessage(null); + assertTrue(contextMessage.indexOf(TEST_MESSAGE) == -1); + } + + public void testGetFirstContextValue() { + exceptionContext.addContextValue("test2", "different value"); + + assertTrue(exceptionContext.getFirstContextValue("test1") == null); + assertTrue(exceptionContext.getFirstContextValue("test2").equals("some value")); + assertTrue(exceptionContext.getFirstContextValue("crap") == null); + + exceptionContext.setContextValue("test2", "another"); + + assertTrue(exceptionContext.getFirstContextValue("test2").equals("another")); + } + + public void testGetContextValues() { + exceptionContext.addContextValue("test2", "different value"); + + assertEquals(exceptionContext.getContextValues("test1"), Collections.singletonList(null)); + assertEquals(exceptionContext.getContextValues("test2"), Arrays.asList("some value", "different value")); + + exceptionContext.setContextValue("test2", "another"); + + assertTrue(exceptionContext.getFirstContextValue("test2").equals("another")); + } + + public void testGetContextLabels() { + assertEquals(5, exceptionContext.getContextEntries().size()); + + exceptionContext.addContextValue("test2", "different value"); + + Set labels = exceptionContext.getContextLabels(); + assertEquals(6, exceptionContext.getContextEntries().size()); + assertEquals(5, labels.size()); + assertTrue(labels.contains("test1")); + assertTrue(labels.contains("test2")); + assertTrue(labels.contains("test Date")); + assertTrue(labels.contains("test Nbr")); + } + + public void testGetContextEntries() { + assertEquals(5, exceptionContext.getContextEntries().size()); + + exceptionContext.addContextValue("test2", "different value"); + + List> entries = exceptionContext.getContextEntries(); + assertEquals(6, entries.size()); + assertEquals("test1", entries.get(0).getKey()); + assertEquals("test2", entries.get(1).getKey()); + assertEquals("test Date", entries.get(2).getKey()); + assertEquals("test Nbr", entries.get(3).getKey()); + assertEquals("test Poorly written obj", entries.get(4).getKey()); + assertEquals("test2", entries.get(5).getKey()); + } + + public void testJavaSerialization() { + exceptionContext.setContextValue("test Poorly written obj", "serializable replacement"); + + @SuppressWarnings("unchecked") + T clone = SerializationUtils.deserialize(SerializationUtils.serialize(exceptionContext)); + assertEquals(exceptionContext.getFormattedExceptionMessage(null), clone.getFormattedExceptionMessage(null)); + } +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/exception/CloneFailedException.java b/ApacheCommonsLang/org/apache/commons/lang3/exception/CloneFailedException.java new file mode 100644 index 0000000..0c324e9 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/exception/CloneFailedException.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.exception; + +/** + * Exception thrown when a clone cannot be created. In contrast to + * {@link CloneNotSupportedException} this is a {@link RuntimeException}. + * + * @since 3.0 + */ +public class CloneFailedException extends RuntimeException { + // ~ Static fields/initializers --------------------------------------------- + + private static final long serialVersionUID = 20091223L; + + // ~ Constructors ----------------------------------------------------------- + + /** + * Constructs a CloneFailedException. + * + * @param message description of the exception + * @since upcoming + */ + public CloneFailedException(final String message) { + super(message); + } + + /** + * Constructs a CloneFailedException. + * + * @param cause cause of the exception + * @since upcoming + */ + public CloneFailedException(final Throwable cause) { + super(cause); + } + + /** + * Constructs a CloneFailedException. + * + * @param message description of the exception + * @param cause cause of the exception + * @since upcoming + */ + public CloneFailedException(final String message, final Throwable cause) { + super(message, cause); + } +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/exception/ContextedException.java b/ApacheCommonsLang/org/apache/commons/lang3/exception/ContextedException.java new file mode 100644 index 0000000..2c9edb4 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/exception/ContextedException.java @@ -0,0 +1,253 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.exception; + +import java.util.List; +import java.util.Set; + +import org.apache.commons.lang3.tuple.Pair; + +/** + *

+ * An exception that provides an easy and safe way to add contextual information. + *

+ * An exception trace itself is often insufficient to provide rapid diagnosis of the issue. + * Frequently what is needed is a select few pieces of local contextual data. + * Providing this data is tricky however, due to concerns over formatting and nulls. + *

+ * The contexted exception approach allows the exception to be created together with a + * list of context label-value pairs. This additional information is automatically included in + * the message and printed stack trace. + *

+ * An unchecked version of this exception is provided by ContextedRuntimeException. + *

+ *

+ * To use this class write code as follows: + *

+ *
+ *   try {
+ *     ...
+ *   } catch (Exception e) {
+ *     throw new ContextedException("Error posting account transaction", e)
+ *          .addContextValue("Account Number", accountNumber)
+ *          .addContextValue("Amount Posted", amountPosted)
+ *          .addContextValue("Previous Balance", previousBalance)
+ *   }
+ * }
+ * 
or improve diagnose data at a higher level: + *
+ *   try {
+ *     ...
+ *   } catch (ContextedException e) {
+ *     throw e.setContextValue("Transaction Id", transactionId);
+ *   } catch (Exception e) {
+ *     if (e instanceof ExceptionContext) {
+ *       e.setContextValue("Transaction Id", transactionId);
+ *     }
+ *     throw e;
+ *   }
+ * }
+ * 
+ *

+ * The output in a printStacktrace() (which often is written to a log) would look something like the following: + *

+ * org.apache.commons.lang3.exception.ContextedException: java.lang.Exception: Error posting account transaction
+ *  Exception Context:
+ *  [1:Account Number=null]
+ *  [2:Amount Posted=100.00]
+ *  [3:Previous Balance=-2.17]
+ *  [4:Transaction Id=94ef1d15-d443-46c4-822b-637f26244899]
+ *
+ *  ---------------------------------
+ *  at org.apache.commons.lang3.exception.ContextedExceptionTest.testAddValue(ContextedExceptionTest.java:88)
+ *  ..... (rest of trace)
+ * 
+ *

+ * + * @see ContextedRuntimeException + * @since 3.0 + */ +public class ContextedException extends Exception implements ExceptionContext { + + /** The serialization version. */ + private static final long serialVersionUID = 20110706L; + /** The context where the data is stored. */ + private final ExceptionContext exceptionContext; + + /** + * Instantiates ContextedException without message or cause. + *

+ * The context information is stored using a default implementation. + */ + public ContextedException() { + super(); + exceptionContext = new DefaultExceptionContext(); + } + + /** + * Instantiates ContextedException with message, but without cause. + *

+ * The context information is stored using a default implementation. + * + * @param message the exception message, may be null + */ + public ContextedException(String message) { + super(message); + exceptionContext = new DefaultExceptionContext(); + } + + /** + * Instantiates ContextedException with cause, but without message. + *

+ * The context information is stored using a default implementation. + * + * @param cause the underlying cause of the exception, may be null + */ + public ContextedException(Throwable cause) { + super(cause); + exceptionContext = new DefaultExceptionContext(); + } + + /** + * Instantiates ContextedException with cause and message. + *

+ * The context information is stored using a default implementation. + * + * @param message the exception message, may be null + * @param cause the underlying cause of the exception, may be null + */ + public ContextedException(String message, Throwable cause) { + super(message, cause); + exceptionContext = new DefaultExceptionContext(); + } + + /** + * Instantiates ContextedException with cause, message, and ExceptionContext. + * + * @param message the exception message, may be null + * @param cause the underlying cause of the exception, may be null + * @param context the context used to store the additional information, null uses default implementation + */ + public ContextedException(String message, Throwable cause, ExceptionContext context) { + super(message, cause); + if (context == null) { + context = new DefaultExceptionContext(); + } + exceptionContext = context; + } + + //----------------------------------------------------------------------- + /** + * Adds information helpful to a developer in diagnosing and correcting the problem. + * For the information to be meaningful, the value passed should have a reasonable + * toString() implementation. + * Different values can be added with the same label multiple times. + *

+ * Note: This exception is only serializable if the object added is serializable. + *

+ * + * @param label a textual label associated with information, {@code null} not recommended + * @param value information needed to understand exception, may be {@code null} + * @return {@code this}, for method chaining, not {@code null} + */ + @Override + public ContextedException addContextValue(String label, Object value) { + exceptionContext.addContextValue(label, value); + return this; + } + + /** + * Sets information helpful to a developer in diagnosing and correcting the problem. + * For the information to be meaningful, the value passed should have a reasonable + * toString() implementation. + * Any existing values with the same labels are removed before the new one is added. + *

+ * Note: This exception is only serializable if the object added as value is serializable. + *

+ * + * @param label a textual label associated with information, {@code null} not recommended + * @param value information needed to understand exception, may be {@code null} + * @return {@code this}, for method chaining, not {@code null} + */ + @Override + public ContextedException setContextValue(String label, Object value) { + exceptionContext.setContextValue(label, value); + return this; + } + + /** + * {@inheritDoc} + */ + @Override + public List getContextValues(String label) { + return this.exceptionContext.getContextValues(label); + } + + /** + * {@inheritDoc} + */ + @Override + public Object getFirstContextValue(String label) { + return this.exceptionContext.getFirstContextValue(label); + } + + /** + * {@inheritDoc} + */ + @Override + public List> getContextEntries() { + return this.exceptionContext.getContextEntries(); + } + + /** + * {@inheritDoc} + */ + @Override + public Set getContextLabels() { + return exceptionContext.getContextLabels(); + } + + /** + * Provides the message explaining the exception, including the contextual data. + * + * @see java.lang.Throwable#getMessage() + * @return the message, never null + */ + @Override + public String getMessage(){ + return getFormattedExceptionMessage(super.getMessage()); + } + + /** + * Provides the message explaining the exception without the contextual data. + * + * @see java.lang.Throwable#getMessage() + * @return the message + * @since 3.0.1 + */ + public String getRawMessage() { + return super.getMessage(); + } + + /** + * {@inheritDoc} + */ + @Override + public String getFormattedExceptionMessage(String baseMessage) { + return exceptionContext.getFormattedExceptionMessage(baseMessage); + } +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/exception/ContextedExceptionTest.java b/ApacheCommonsLang/org/apache/commons/lang3/exception/ContextedExceptionTest.java new file mode 100644 index 0000000..267938c --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/exception/ContextedExceptionTest.java @@ -0,0 +1,98 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.exception; + +import java.util.Date; + +import org.apache.commons.lang3.StringUtils; + +/** + * JUnit tests for ContextedException. + */ +public class ContextedExceptionTest extends AbstractExceptionContextTest { + + @Override + public void setUp() throws Exception { + exceptionContext = new ContextedException(new Exception(TEST_MESSAGE)); + super.setUp(); + } + + public void testContextedException() { + exceptionContext = new ContextedException(); + String message = exceptionContext.getMessage(); + String trace = ExceptionUtils.getStackTrace(exceptionContext); + assertTrue(trace.indexOf("ContextedException")>=0); + assertTrue(StringUtils.isEmpty(message)); + } + + public void testContextedExceptionString() { + exceptionContext = new ContextedException(TEST_MESSAGE); + assertEquals(TEST_MESSAGE, exceptionContext.getMessage()); + + String trace = ExceptionUtils.getStackTrace(exceptionContext); + assertTrue(trace.indexOf(TEST_MESSAGE)>=0); + } + + public void testContextedExceptionThrowable() { + exceptionContext = new ContextedException(new Exception(TEST_MESSAGE)); + String message = exceptionContext.getMessage(); + String trace = ExceptionUtils.getStackTrace(exceptionContext); + assertTrue(trace.indexOf("ContextedException")>=0); + assertTrue(trace.indexOf(TEST_MESSAGE)>=0); + assertTrue(message.indexOf(TEST_MESSAGE)>=0); + } + + public void testContextedExceptionStringThrowable() { + exceptionContext = new ContextedException(TEST_MESSAGE_2, new Exception(TEST_MESSAGE)); + String message = exceptionContext.getMessage(); + String trace = ExceptionUtils.getStackTrace(exceptionContext); + assertTrue(trace.indexOf("ContextedException")>=0); + assertTrue(trace.indexOf(TEST_MESSAGE)>=0); + assertTrue(trace.indexOf(TEST_MESSAGE_2)>=0); + assertTrue(message.indexOf(TEST_MESSAGE_2)>=0); + } + + public void testContextedExceptionStringThrowableContext() { + exceptionContext = new ContextedException(TEST_MESSAGE_2, new Exception(TEST_MESSAGE), new DefaultExceptionContext()); + String message = exceptionContext.getMessage(); + String trace = ExceptionUtils.getStackTrace(exceptionContext); + assertTrue(trace.indexOf("ContextedException")>=0); + assertTrue(trace.indexOf(TEST_MESSAGE)>=0); + assertTrue(trace.indexOf(TEST_MESSAGE_2)>=0); + assertTrue(message.indexOf(TEST_MESSAGE_2)>=0); + } + + public void testNullExceptionPassing() { + exceptionContext = new ContextedException(TEST_MESSAGE_2, new Exception(TEST_MESSAGE), null) + .addContextValue("test1", null) + .addContextValue("test2", "some value") + .addContextValue("test Date", new Date()) + .addContextValue("test Nbr", Integer.valueOf(5)) + .addContextValue("test Poorly written obj", new ObjectWithFaultyToString()); + + String message = exceptionContext.getMessage(); + assertTrue(message != null); + } + + public void testRawMessage() { + assertEquals(Exception.class.getName() + ": " + TEST_MESSAGE, exceptionContext.getRawMessage()); + exceptionContext = new ContextedException(TEST_MESSAGE_2, new Exception(TEST_MESSAGE), new DefaultExceptionContext()); + assertEquals(TEST_MESSAGE_2, exceptionContext.getRawMessage()); + exceptionContext = new ContextedException(null, new Exception(TEST_MESSAGE), new DefaultExceptionContext()); + assertNull(exceptionContext.getRawMessage()); + } +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/exception/ContextedRuntimeException.java b/ApacheCommonsLang/org/apache/commons/lang3/exception/ContextedRuntimeException.java new file mode 100644 index 0000000..56b9dd4 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/exception/ContextedRuntimeException.java @@ -0,0 +1,254 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.exception; + +import java.util.List; +import java.util.Set; + +import org.apache.commons.lang3.tuple.Pair; + +/** + *

+ * A runtime exception that provides an easy and safe way to add contextual information. + *

+ * An exception trace itself is often insufficient to provide rapid diagnosis of the issue. + * Frequently what is needed is a select few pieces of local contextual data. + * Providing this data is tricky however, due to concerns over formatting and nulls. + *

+ * The contexted exception approach allows the exception to be created together with a + * list of context label-value pairs. This additional information is automatically included in + * the message and printed stack trace. + *

+ * A checked version of this exception is provided by ContextedException. + *

+ *

+ * To use this class write code as follows: + *

+ *
+ *   try {
+ *     ...
+ *   } catch (Exception e) {
+ *     throw new ContextedRuntimeException("Error posting account transaction", e)
+ *          .addContextValue("Account Number", accountNumber)
+ *          .addContextValue("Amount Posted", amountPosted)
+ *          .addContextValue("Previous Balance", previousBalance)
+ *   }
+ * }
+ * 
or improve diagnose data at a higher level: + *
+ *   try {
+ *     ...
+ *   } catch (ContextedRuntimeException e) {
+ *     throw e.setContextValue("Transaction Id", transactionId);
+ *   } catch (Exception e) {
+ *     if (e instanceof ExceptionContext) {
+ *       e.setContextValue("Transaction Id", transactionId);
+ *     }
+ *     throw e;
+ *   }
+ * }
+ * 
+ *

+ * The output in a printStacktrace() (which often is written to a log) would look something like the following: + *

+ * org.apache.commons.lang3.exception.ContextedRuntimeException: java.lang.Exception: Error posting account transaction
+ *  Exception Context:
+ *  [1:Account Number=null]
+ *  [2:Amount Posted=100.00]
+ *  [3:Previous Balance=-2.17]
+ *  [4:Transaction Id=94ef1d15-d443-46c4-822b-637f26244899]
+ *
+ *  ---------------------------------
+ *  at org.apache.commons.lang3.exception.ContextedRuntimeExceptionTest.testAddValue(ContextedExceptionTest.java:88)
+ *  ..... (rest of trace)
+ * 
+ *

+ * + * @see ContextedException + * @since 3.0 + */ +public class ContextedRuntimeException extends RuntimeException implements ExceptionContext { + + /** The serialization version. */ + private static final long serialVersionUID = 20110706L; + /** The context where the data is stored. */ + private final ExceptionContext exceptionContext; + + /** + * Instantiates ContextedRuntimeException without message or cause. + *

+ * The context information is stored using a default implementation. + */ + public ContextedRuntimeException() { + super(); + exceptionContext = new DefaultExceptionContext(); + } + + /** + * Instantiates ContextedRuntimeException with message, but without cause. + *

+ * The context information is stored using a default implementation. + * + * @param message the exception message, may be null + */ + public ContextedRuntimeException(String message) { + super(message); + exceptionContext = new DefaultExceptionContext(); + } + + /** + * Instantiates ContextedRuntimeException with cause, but without message. + *

+ * The context information is stored using a default implementation. + * + * @param cause the underlying cause of the exception, may be null + */ + public ContextedRuntimeException(Throwable cause) { + super(cause); + exceptionContext = new DefaultExceptionContext(); + } + + /** + * Instantiates ContextedRuntimeException with cause and message. + *

+ * The context information is stored using a default implementation. + * + * @param message the exception message, may be null + * @param cause the underlying cause of the exception, may be null + */ + public ContextedRuntimeException(String message, Throwable cause) { + super(message, cause); + exceptionContext = new DefaultExceptionContext(); + } + + /** + * Instantiates ContextedRuntimeException with cause, message, and ExceptionContext. + * + * @param message the exception message, may be null + * @param cause the underlying cause of the exception, may be null + * @param context the context used to store the additional information, null uses default implementation + */ + public ContextedRuntimeException(String message, Throwable cause, ExceptionContext context) { + super(message, cause); + if (context == null) { + context = new DefaultExceptionContext(); + } + exceptionContext = context; + } + + //----------------------------------------------------------------------- + /** + * Adds information helpful to a developer in diagnosing and correcting the problem. + * For the information to be meaningful, the value passed should have a reasonable + * toString() implementation. + * Different values can be added with the same label multiple times. + *

+ * Note: This exception is only serializable if the object added is serializable. + *

+ * + * @param label a textual label associated with information, {@code null} not recommended + * @param value information needed to understand exception, may be {@code null} + * @return {@code this}, for method chaining, not {@code null} + */ + @Override + public ContextedRuntimeException addContextValue(String label, Object value) { + exceptionContext.addContextValue(label, value); + return this; + } + + /** + * Sets information helpful to a developer in diagnosing and correcting the problem. + * For the information to be meaningful, the value passed should have a reasonable + * toString() implementation. + * Any existing values with the same labels are removed before the new one is added. + *

+ * Note: This exception is only serializable if the object added as value is serializable. + *

+ * + * @param label a textual label associated with information, {@code null} not recommended + * @param value information needed to understand exception, may be {@code null} + * @return {@code this}, for method chaining, not {@code null} + */ + @Override + public ContextedRuntimeException setContextValue(String label, Object value) { + exceptionContext.setContextValue(label, value); + return this; + } + + /** + * {@inheritDoc} + */ + @Override + public List getContextValues(String label) { + return this.exceptionContext.getContextValues(label); + } + + /** + * {@inheritDoc} + */ + @Override + public Object getFirstContextValue(String label) { + return this.exceptionContext.getFirstContextValue(label); + } + + /** + * {@inheritDoc} + */ + @Override + public List> getContextEntries() { + return this.exceptionContext.getContextEntries(); + } + + /** + * {@inheritDoc} + */ + @Override + public Set getContextLabels() { + return exceptionContext.getContextLabels(); + } + + /** + * Provides the message explaining the exception, including the contextual data. + * + * @see java.lang.Throwable#getMessage() + * @return the message, never null + */ + @Override + public String getMessage(){ + return getFormattedExceptionMessage(super.getMessage()); + } + + /** + * Provides the message explaining the exception without the contextual data. + * + * @see java.lang.Throwable#getMessage() + * @return the message + * @since 3.0.1 + */ + public String getRawMessage() { + return super.getMessage(); + } + + /** + * {@inheritDoc} + */ + @Override + public String getFormattedExceptionMessage(String baseMessage) { + return exceptionContext.getFormattedExceptionMessage(baseMessage); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/exception/ContextedRuntimeExceptionTest.java b/ApacheCommonsLang/org/apache/commons/lang3/exception/ContextedRuntimeExceptionTest.java new file mode 100644 index 0000000..556f800 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/exception/ContextedRuntimeExceptionTest.java @@ -0,0 +1,98 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.exception; + +import java.util.Date; + +import org.apache.commons.lang3.StringUtils; + +/** + * JUnit tests for ContextedRuntimeException. + */ +public class ContextedRuntimeExceptionTest extends AbstractExceptionContextTest { + + @Override + protected void setUp() throws Exception { + exceptionContext = new ContextedRuntimeException(new Exception(TEST_MESSAGE)); + super.setUp(); + } + + public void testContextedException() { + exceptionContext = new ContextedRuntimeException(); + String message = exceptionContext.getMessage(); + String trace = ExceptionUtils.getStackTrace(exceptionContext); + assertTrue(trace.indexOf("ContextedException")>=0); + assertTrue(StringUtils.isEmpty(message)); + } + + public void testContextedExceptionString() { + exceptionContext = new ContextedRuntimeException(TEST_MESSAGE); + assertEquals(TEST_MESSAGE, exceptionContext.getMessage()); + + String trace = ExceptionUtils.getStackTrace(exceptionContext); + assertTrue(trace.indexOf(TEST_MESSAGE)>=0); + } + + public void testContextedExceptionThrowable() { + exceptionContext = new ContextedRuntimeException(new Exception(TEST_MESSAGE)); + String message = exceptionContext.getMessage(); + String trace = ExceptionUtils.getStackTrace(exceptionContext); + assertTrue(trace.indexOf("ContextedException")>=0); + assertTrue(trace.indexOf(TEST_MESSAGE)>=0); + assertTrue(message.indexOf(TEST_MESSAGE)>=0); + } + + public void testContextedExceptionStringThrowable() { + exceptionContext = new ContextedRuntimeException(TEST_MESSAGE_2, new Exception(TEST_MESSAGE)); + String message = exceptionContext.getMessage(); + String trace = ExceptionUtils.getStackTrace(exceptionContext); + assertTrue(trace.indexOf("ContextedException")>=0); + assertTrue(trace.indexOf(TEST_MESSAGE)>=0); + assertTrue(trace.indexOf(TEST_MESSAGE_2)>=0); + assertTrue(message.indexOf(TEST_MESSAGE_2)>=0); + } + + public void testContextedExceptionStringThrowableContext() { + exceptionContext = new ContextedRuntimeException(TEST_MESSAGE_2, new Exception(TEST_MESSAGE), new DefaultExceptionContext() {}); + String message = exceptionContext.getMessage(); + String trace = ExceptionUtils.getStackTrace(exceptionContext); + assertTrue(trace.indexOf("ContextedException")>=0); + assertTrue(trace.indexOf(TEST_MESSAGE)>=0); + assertTrue(trace.indexOf(TEST_MESSAGE_2)>=0); + assertTrue(message.indexOf(TEST_MESSAGE_2)>=0); + } + + public void testNullExceptionPassing() { + exceptionContext = new ContextedRuntimeException(TEST_MESSAGE_2, new Exception(TEST_MESSAGE), null) + .addContextValue("test1", null) + .addContextValue("test2", "some value") + .addContextValue("test Date", new Date()) + .addContextValue("test Nbr", Integer.valueOf(5)) + .addContextValue("test Poorly written obj", new ObjectWithFaultyToString()); + + String message = exceptionContext.getMessage(); + assertTrue(message != null); + } + + public void testRawMessage() { + assertEquals(Exception.class.getName() + ": " + TEST_MESSAGE, exceptionContext.getRawMessage()); + exceptionContext = new ContextedRuntimeException(TEST_MESSAGE_2, new Exception(TEST_MESSAGE), new DefaultExceptionContext()); + assertEquals(TEST_MESSAGE_2, exceptionContext.getRawMessage()); + exceptionContext = new ContextedRuntimeException(null, new Exception(TEST_MESSAGE), new DefaultExceptionContext()); + assertNull(exceptionContext.getRawMessage()); + } +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/exception/DefaultExceptionContext.java b/ApacheCommonsLang/org/apache/commons/lang3/exception/DefaultExceptionContext.java new file mode 100644 index 0000000..8d3b25c --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/exception/DefaultExceptionContext.java @@ -0,0 +1,165 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.exception; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.apache.commons.lang3.tuple.Pair; + +/** + * Default implementation of the context storing the label-value pairs for contexted exceptions. + *

+ * This implementation is serializable, however this is dependent on the values that + * are added also being serializable. + *

+ * + * @see ContextedException + * @see ContextedRuntimeException + * @since 3.0 + */ +public class DefaultExceptionContext implements ExceptionContext, Serializable { + + /** The serialization version. */ + private static final long serialVersionUID = 20110706L; + + /** The list storing the label-data pairs. */ + private final List> contextValues = new ArrayList>(); + + /** + * {@inheritDoc} + */ + @Override + public DefaultExceptionContext addContextValue(String label, Object value) { + contextValues.add(new ImmutablePair(label, value)); + return this; + } + + /** + * {@inheritDoc} + */ + @Override + public DefaultExceptionContext setContextValue(String label, Object value) { + for (final Iterator> iter = contextValues.iterator(); iter.hasNext();) { + final Pair p = iter.next(); + if (StringUtils.equals(label, p.getKey())) { + iter.remove(); + } + } + addContextValue(label, value); + return this; + } + + /** + * {@inheritDoc} + */ + @Override + public List getContextValues(String label) { + final List values = new ArrayList(); + for (final Pair pair : contextValues) { + if (StringUtils.equals(label, pair.getKey())) { + values.add(pair.getValue()); + } + } + return values; + } + + /** + * {@inheritDoc} + */ + @Override + public Object getFirstContextValue(String label) { + for (final Pair pair : contextValues) { + if (StringUtils.equals(label, pair.getKey())) { + return pair.getValue(); + } + } + return null; + } + + /** + * {@inheritDoc} + */ + @Override + public Set getContextLabels() { + final Set labels = new HashSet(); + for (final Pair pair : contextValues) { + labels.add(pair.getKey()); + } + return labels; + } + + /** + * {@inheritDoc} + */ + @Override + public List> getContextEntries() { + return contextValues; + } + + /** + * Builds the message containing the contextual information. + * + * @param baseMessage the base exception message without context information appended + * @return the exception message with context information appended, never null + */ + @Override + public String getFormattedExceptionMessage(String baseMessage){ + StringBuilder buffer = new StringBuilder(256); + if (baseMessage != null) { + buffer.append(baseMessage); + } + + if (contextValues.size() > 0) { + if (buffer.length() > 0) { + buffer.append('\n'); + } + buffer.append("Exception Context:\n"); + + int i = 0; + for (final Pair pair : contextValues) { + buffer.append("\t["); + buffer.append(++i); + buffer.append(':'); + buffer.append(pair.getKey()); + buffer.append("="); + final Object value = pair.getValue(); + if (value == null) { + buffer.append("null"); + } else { + String valueStr; + try { + valueStr = value.toString(); + } catch (Exception e) { + valueStr = "Exception thrown on toString(): " + ExceptionUtils.getStackTrace(e); + } + buffer.append(valueStr); + } + buffer.append("]\n"); + } + buffer.append("---------------------------------"); + } + return buffer.toString(); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/exception/DefaultExceptionContextTest.java b/ApacheCommonsLang/org/apache/commons/lang3/exception/DefaultExceptionContextTest.java new file mode 100644 index 0000000..8e46590 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/exception/DefaultExceptionContextTest.java @@ -0,0 +1,36 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.exception; + +/** + * JUnit tests for DefaultExceptionContext. + * + */ +public class DefaultExceptionContextTest extends AbstractExceptionContextTest { + + @Override + public void setUp() throws Exception { + exceptionContext = new DefaultExceptionContext(); + super.setUp(); + } + + public void testFormattedExceptionMessageNull() { + exceptionContext = new DefaultExceptionContext(); + exceptionContext.getFormattedExceptionMessage(null); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/exception/ExceptionContext.java b/ApacheCommonsLang/org/apache/commons/lang3/exception/ExceptionContext.java new file mode 100644 index 0000000..d8076c8 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/exception/ExceptionContext.java @@ -0,0 +1,103 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.exception; + +import java.util.List; +import java.util.Set; + +import org.apache.commons.lang3.tuple.Pair; + +/** + * Allows the storage and retrieval of contextual information based on label-value + * pairs for exceptions. + *

+ * Implementations are expected to manage the pairs in a list-style collection + * that keeps the pairs in the sequence of their addition. + *

+ * + * @see ContextedException + * @see ContextedRuntimeException + * @since 3.0 + */ +public interface ExceptionContext { + + /** + * Adds a contextual label-value pair into this context. + *

+ * The pair will be added to the context, independently of an already + * existing pair with the same label. + *

+ * + * @param label the label of the item to add, {@code null} not recommended + * @param value the value of item to add, may be {@code null} + * @return {@code this}, for method chaining, not {@code null} + */ + public ExceptionContext addContextValue(String label, Object value); + + /** + * Sets a contextual label-value pair into this context. + *

+ * The pair will be added normally, but any existing label-value pair with + * the same label is removed from the context. + *

+ * + * @param label the label of the item to add, {@code null} not recommended + * @param value the value of item to add, may be {@code null} + * @return {@code this}, for method chaining, not {@code null} + */ + public ExceptionContext setContextValue(String label, Object value); + + /** + * Retrieves all the contextual data values associated with the label. + * + * @param label the label to get the contextual values for, may be {@code null} + * @return the contextual values associated with the label, never {@code null} + */ + public List getContextValues(String label); + + /** + * Retrieves the first available contextual data value associated with the label. + * + * @param label the label to get the contextual value for, may be {@code null} + * @return the first contextual value associated with the label, may be {@code null} + */ + public Object getFirstContextValue(String label); + + /** + * Retrieves the full set of labels defined in the contextual data. + * + * @return the set of labels, not {@code null} + */ + public Set getContextLabels(); + + /** + * Retrieves the full list of label-value pairs defined in the contextual data. + * + * @return the list of pairs, not {@code null} + */ + public List> getContextEntries(); + + /** + * Gets the contextualized error message based on a base message. + * This will add the context label-value pairs to the message. + * + * @param baseMessage the base exception message without context information appended + * @return the exception message with context information appended, not {@code null} + */ + public String getFormattedExceptionMessage(String baseMessage); + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/exception/ExceptionUtils.java b/ApacheCommonsLang/org/apache/commons/lang3/exception/ExceptionUtils.java new file mode 100644 index 0000000..83e7812 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/exception/ExceptionUtils.java @@ -0,0 +1,697 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.exception; + +import java.io.PrintStream; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.StringTokenizer; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.ClassUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.SystemUtils; + +/** + *

Provides utilities for manipulating and examining + * Throwable objects.

+ * + * @since 1.0 + * @version $Id: ExceptionUtils.java 1299411 2012-03-11 17:55:29Z ggregory $ + */ +public class ExceptionUtils { + + /** + *

Used when printing stack frames to denote the start of a + * wrapped exception.

+ * + *

Package private for accessibility by test suite.

+ */ + static final String WRAPPED_MARKER = " [wrapped] "; + + /** + *

The names of methods commonly used to access a wrapped exception.

+ */ + // TODO: Remove in Lang 4.0 + private static final String[] CAUSE_METHOD_NAMES = { + "getCause", + "getNextException", + "getTargetException", + "getException", + "getSourceException", + "getRootCause", + "getCausedByException", + "getNested", + "getLinkedException", + "getNestedException", + "getLinkedCause", + "getThrowable", + }; + + /** + *

+ * Public constructor allows an instance of ExceptionUtils to be created, although that is not + * normally necessary. + *

+ */ + public ExceptionUtils() { + super(); + } + + //----------------------------------------------------------------------- + /** + *

Returns the default names used when searching for the cause of an exception.

+ * + *

This may be modified and used in the overloaded getCause(Throwable, String[]) method.

+ * + * @return cloned array of the default method names + * @since 3.0 + * @deprecated This feature will be removed in Lang 4.0 + */ + @Deprecated + public static String[] getDefaultCauseMethodNames() { + return ArrayUtils.clone(CAUSE_METHOD_NAMES); + } + + //----------------------------------------------------------------------- + /** + *

Introspects the Throwable to obtain the cause.

+ * + *

The method searches for methods with specific names that return a + * Throwable object. This will pick up most wrapping exceptions, + * including those from JDK 1.4. + * + *

The default list searched for are:

+ *
    + *
  • getCause()
  • + *
  • getNextException()
  • + *
  • getTargetException()
  • + *
  • getException()
  • + *
  • getSourceException()
  • + *
  • getRootCause()
  • + *
  • getCausedByException()
  • + *
  • getNested()
  • + *
+ * + *

If none of the above is found, returns null.

+ * + * @param throwable the throwable to introspect for a cause, may be null + * @return the cause of the Throwable, + * null if none found or null throwable input + * @since 1.0 + * @deprecated This feature will be removed in Lang 4.0 + */ + @Deprecated + public static Throwable getCause(Throwable throwable) { + return getCause(throwable, CAUSE_METHOD_NAMES); + } + + /** + *

Introspects the Throwable to obtain the cause.

+ * + *

A null set of method names means use the default set. + * A null in the set of method names will be ignored.

+ * + * @param throwable the throwable to introspect for a cause, may be null + * @param methodNames the method names, null treated as default set + * @return the cause of the Throwable, + * null if none found or null throwable input + * @since 1.0 + * @deprecated This feature will be removed in Lang 4.0 + */ + @Deprecated + public static Throwable getCause(Throwable throwable, String[] methodNames) { + if (throwable == null) { + return null; + } + + if (methodNames == null) { + methodNames = CAUSE_METHOD_NAMES; + } + + for (String methodName : methodNames) { + if (methodName != null) { + Throwable cause = getCauseUsingMethodName(throwable, methodName); + if (cause != null) { + return cause; + } + } + } + + return null; + } + + /** + *

Introspects the Throwable to obtain the root cause.

+ * + *

This method walks through the exception chain to the last element, + * "root" of the tree, using {@link #getCause(Throwable)}, and + * returns that exception.

+ * + *

From version 2.2, this method handles recursive cause structures + * that might otherwise cause infinite loops. If the throwable parameter + * has a cause of itself, then null will be returned. If the throwable + * parameter cause chain loops, the last element in the chain before the + * loop is returned.

+ * + * @param throwable the throwable to get the root cause for, may be null + * @return the root cause of the Throwable, + * null if none found or null throwable input + */ + public static Throwable getRootCause(Throwable throwable) { + List list = getThrowableList(throwable); + return list.size() < 2 ? null : (Throwable)list.get(list.size() - 1); + } + + /** + *

Finds a Throwable by method name.

+ * + * @param throwable the exception to examine + * @param methodName the name of the method to find and invoke + * @return the wrapped exception, or null if not found + */ + // TODO: Remove in Lang 4.0 + private static Throwable getCauseUsingMethodName(Throwable throwable, String methodName) { + Method method = null; + try { + method = throwable.getClass().getMethod(methodName); + } catch (NoSuchMethodException ignored) { // NOPMD + // exception ignored + } catch (SecurityException ignored) { // NOPMD + // exception ignored + } + + if (method != null && Throwable.class.isAssignableFrom(method.getReturnType())) { + try { + return (Throwable) method.invoke(throwable); + } catch (IllegalAccessException ignored) { // NOPMD + // exception ignored + } catch (IllegalArgumentException ignored) { // NOPMD + // exception ignored + } catch (InvocationTargetException ignored) { // NOPMD + // exception ignored + } + } + return null; + } + + //----------------------------------------------------------------------- + /** + *

Counts the number of Throwable objects in the + * exception chain.

+ * + *

A throwable without cause will return 1. + * A throwable with one cause will return 2 and so on. + * A null throwable will return 0.

+ * + *

From version 2.2, this method handles recursive cause structures + * that might otherwise cause infinite loops. The cause chain is + * processed until the end is reached, or until the next item in the + * chain is already in the result set.

+ * + * @param throwable the throwable to inspect, may be null + * @return the count of throwables, zero if null input + */ + public static int getThrowableCount(Throwable throwable) { + return getThrowableList(throwable).size(); + } + + /** + *

Returns the list of Throwable objects in the + * exception chain.

+ * + *

A throwable without cause will return an array containing + * one element - the input throwable. + * A throwable with one cause will return an array containing + * two elements. - the input throwable and the cause throwable. + * A null throwable will return an array of size zero.

+ * + *

From version 2.2, this method handles recursive cause structures + * that might otherwise cause infinite loops. The cause chain is + * processed until the end is reached, or until the next item in the + * chain is already in the result set.

+ * + * @see #getThrowableList(Throwable) + * @param throwable the throwable to inspect, may be null + * @return the array of throwables, never null + */ + public static Throwable[] getThrowables(Throwable throwable) { + List list = getThrowableList(throwable); + return list.toArray(new Throwable[list.size()]); + } + + /** + *

Returns the list of Throwable objects in the + * exception chain.

+ * + *

A throwable without cause will return a list containing + * one element - the input throwable. + * A throwable with one cause will return a list containing + * two elements. - the input throwable and the cause throwable. + * A null throwable will return a list of size zero.

+ * + *

This method handles recursive cause structures that might + * otherwise cause infinite loops. The cause chain is processed until + * the end is reached, or until the next item in the chain is already + * in the result set.

+ * + * @param throwable the throwable to inspect, may be null + * @return the list of throwables, never null + * @since Commons Lang 2.2 + */ + public static List getThrowableList(Throwable throwable) { + List list = new ArrayList(); + while (throwable != null && list.contains(throwable) == false) { + list.add(throwable); + throwable = ExceptionUtils.getCause(throwable); + } + return list; + } + + //----------------------------------------------------------------------- + /** + *

Returns the (zero based) index of the first Throwable + * that matches the specified class (exactly) in the exception chain. + * Subclasses of the specified class do not match - see + * {@link #indexOfType(Throwable, Class)} for the opposite.

+ * + *

A null throwable returns -1. + * A null type returns -1. + * No match in the chain returns -1.

+ * + * @param throwable the throwable to inspect, may be null + * @param clazz the class to search for, subclasses do not match, null returns -1 + * @return the index into the throwable chain, -1 if no match or null input + */ + public static int indexOfThrowable(Throwable throwable, Class clazz) { + return indexOf(throwable, clazz, 0, false); + } + + /** + *

Returns the (zero based) index of the first Throwable + * that matches the specified type in the exception chain from + * a specified index. + * Subclasses of the specified class do not match - see + * {@link #indexOfType(Throwable, Class, int)} for the opposite.

+ * + *

A null throwable returns -1. + * A null type returns -1. + * No match in the chain returns -1. + * A negative start index is treated as zero. + * A start index greater than the number of throwables returns -1.

+ * + * @param throwable the throwable to inspect, may be null + * @param clazz the class to search for, subclasses do not match, null returns -1 + * @param fromIndex the (zero based) index of the starting position, + * negative treated as zero, larger than chain size returns -1 + * @return the index into the throwable chain, -1 if no match or null input + */ + public static int indexOfThrowable(Throwable throwable, Class clazz, int fromIndex) { + return indexOf(throwable, clazz, fromIndex, false); + } + + //----------------------------------------------------------------------- + /** + *

Returns the (zero based) index of the first Throwable + * that matches the specified class or subclass in the exception chain. + * Subclasses of the specified class do match - see + * {@link #indexOfThrowable(Throwable, Class)} for the opposite.

+ * + *

A null throwable returns -1. + * A null type returns -1. + * No match in the chain returns -1.

+ * + * @param throwable the throwable to inspect, may be null + * @param type the type to search for, subclasses match, null returns -1 + * @return the index into the throwable chain, -1 if no match or null input + * @since 2.1 + */ + public static int indexOfType(Throwable throwable, Class type) { + return indexOf(throwable, type, 0, true); + } + + /** + *

Returns the (zero based) index of the first Throwable + * that matches the specified type in the exception chain from + * a specified index. + * Subclasses of the specified class do match - see + * {@link #indexOfThrowable(Throwable, Class)} for the opposite.

+ * + *

A null throwable returns -1. + * A null type returns -1. + * No match in the chain returns -1. + * A negative start index is treated as zero. + * A start index greater than the number of throwables returns -1.

+ * + * @param throwable the throwable to inspect, may be null + * @param type the type to search for, subclasses match, null returns -1 + * @param fromIndex the (zero based) index of the starting position, + * negative treated as zero, larger than chain size returns -1 + * @return the index into the throwable chain, -1 if no match or null input + * @since 2.1 + */ + public static int indexOfType(Throwable throwable, Class type, int fromIndex) { + return indexOf(throwable, type, fromIndex, true); + } + + /** + *

Worker method for the indexOfType methods.

+ * + * @param throwable the throwable to inspect, may be null + * @param type the type to search for, subclasses match, null returns -1 + * @param fromIndex the (zero based) index of the starting position, + * negative treated as zero, larger than chain size returns -1 + * @param subclass if true, compares with {@link Class#isAssignableFrom(Class)}, otherwise compares + * using references + * @return index of the type within throwables nested withing the specified throwable + */ + private static int indexOf(Throwable throwable, Class type, int fromIndex, boolean subclass) { + if (throwable == null || type == null) { + return -1; + } + if (fromIndex < 0) { + fromIndex = 0; + } + Throwable[] throwables = ExceptionUtils.getThrowables(throwable); + if (fromIndex >= throwables.length) { + return -1; + } + if (subclass) { + for (int i = fromIndex; i < throwables.length; i++) { + if (type.isAssignableFrom(throwables[i].getClass())) { + return i; + } + } + } else { + for (int i = fromIndex; i < throwables.length; i++) { + if (type.equals(throwables[i].getClass())) { + return i; + } + } + } + return -1; + } + + //----------------------------------------------------------------------- + /** + *

Prints a compact stack trace for the root cause of a throwable + * to System.err.

+ * + *

The compact stack trace starts with the root cause and prints + * stack frames up to the place where it was caught and wrapped. + * Then it prints the wrapped exception and continues with stack frames + * until the wrapper exception is caught and wrapped again, etc.

+ * + *

The output of this method is consistent across JDK versions. + * Note that this is the opposite order to the JDK1.4 display.

+ * + *

The method is equivalent to printStackTrace for throwables + * that don't have nested causes.

+ * + * @param throwable the throwable to output + * @since 2.0 + */ + public static void printRootCauseStackTrace(Throwable throwable) { + printRootCauseStackTrace(throwable, System.err); + } + + /** + *

Prints a compact stack trace for the root cause of a throwable.

+ * + *

The compact stack trace starts with the root cause and prints + * stack frames up to the place where it was caught and wrapped. + * Then it prints the wrapped exception and continues with stack frames + * until the wrapper exception is caught and wrapped again, etc.

+ * + *

The output of this method is consistent across JDK versions. + * Note that this is the opposite order to the JDK1.4 display.

+ * + *

The method is equivalent to printStackTrace for throwables + * that don't have nested causes.

+ * + * @param throwable the throwable to output, may be null + * @param stream the stream to output to, may not be null + * @throws IllegalArgumentException if the stream is null + * @since 2.0 + */ + public static void printRootCauseStackTrace(Throwable throwable, PrintStream stream) { + if (throwable == null) { + return; + } + if (stream == null) { + throw new IllegalArgumentException("The PrintStream must not be null"); + } + String trace[] = getRootCauseStackTrace(throwable); + for (String element : trace) { + stream.println(element); + } + stream.flush(); + } + + /** + *

Prints a compact stack trace for the root cause of a throwable.

+ * + *

The compact stack trace starts with the root cause and prints + * stack frames up to the place where it was caught and wrapped. + * Then it prints the wrapped exception and continues with stack frames + * until the wrapper exception is caught and wrapped again, etc.

+ * + *

The output of this method is consistent across JDK versions. + * Note that this is the opposite order to the JDK1.4 display.

+ * + *

The method is equivalent to printStackTrace for throwables + * that don't have nested causes.

+ * + * @param throwable the throwable to output, may be null + * @param writer the writer to output to, may not be null + * @throws IllegalArgumentException if the writer is null + * @since 2.0 + */ + public static void printRootCauseStackTrace(Throwable throwable, PrintWriter writer) { + if (throwable == null) { + return; + } + if (writer == null) { + throw new IllegalArgumentException("The PrintWriter must not be null"); + } + String trace[] = getRootCauseStackTrace(throwable); + for (String element : trace) { + writer.println(element); + } + writer.flush(); + } + + //----------------------------------------------------------------------- + /** + *

Creates a compact stack trace for the root cause of the supplied + * Throwable.

+ * + *

The output of this method is consistent across JDK versions. + * It consists of the root exception followed by each of its wrapping + * exceptions separated by '[wrapped]'. Note that this is the opposite + * order to the JDK1.4 display.

+ * + * @param throwable the throwable to examine, may be null + * @return an array of stack trace frames, never null + * @since 2.0 + */ + public static String[] getRootCauseStackTrace(Throwable throwable) { + if (throwable == null) { + return ArrayUtils.EMPTY_STRING_ARRAY; + } + Throwable throwables[] = getThrowables(throwable); + int count = throwables.length; + List frames = new ArrayList(); + List nextTrace = getStackFrameList(throwables[count - 1]); + for (int i = count; --i >= 0;) { + List trace = nextTrace; + if (i != 0) { + nextTrace = getStackFrameList(throwables[i - 1]); + removeCommonFrames(trace, nextTrace); + } + if (i == count - 1) { + frames.add(throwables[i].toString()); + } else { + frames.add(WRAPPED_MARKER + throwables[i].toString()); + } + for (int j = 0; j < trace.size(); j++) { + frames.add(trace.get(j)); + } + } + return frames.toArray(new String[frames.size()]); + } + + /** + *

Removes common frames from the cause trace given the two stack traces.

+ * + * @param causeFrames stack trace of a cause throwable + * @param wrapperFrames stack trace of a wrapper throwable + * @throws IllegalArgumentException if either argument is null + * @since 2.0 + */ + public static void removeCommonFrames(List causeFrames, List wrapperFrames) { + if (causeFrames == null || wrapperFrames == null) { + throw new IllegalArgumentException("The List must not be null"); + } + int causeFrameIndex = causeFrames.size() - 1; + int wrapperFrameIndex = wrapperFrames.size() - 1; + while (causeFrameIndex >= 0 && wrapperFrameIndex >= 0) { + // Remove the frame from the cause trace if it is the same + // as in the wrapper trace + String causeFrame = causeFrames.get(causeFrameIndex); + String wrapperFrame = wrapperFrames.get(wrapperFrameIndex); + if (causeFrame.equals(wrapperFrame)) { + causeFrames.remove(causeFrameIndex); + } + causeFrameIndex--; + wrapperFrameIndex--; + } + } + + //----------------------------------------------------------------------- + /** + *

Gets the stack trace from a Throwable as a String.

+ * + *

The result of this method vary by JDK version as this method + * uses {@link Throwable#printStackTrace(java.io.PrintWriter)}. + * On JDK1.3 and earlier, the cause exception will not be shown + * unless the specified throwable alters printStackTrace.

+ * + * @param throwable the Throwable to be examined + * @return the stack trace as generated by the exception's + * printStackTrace(PrintWriter) method + */ + public static String getStackTrace(Throwable throwable) { + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw, true); + throwable.printStackTrace(pw); + return sw.getBuffer().toString(); + } + + /** + *

Captures the stack trace associated with the specified + * Throwable object, decomposing it into a list of + * stack frames.

+ * + *

The result of this method vary by JDK version as this method + * uses {@link Throwable#printStackTrace(java.io.PrintWriter)}. + * On JDK1.3 and earlier, the cause exception will not be shown + * unless the specified throwable alters printStackTrace.

+ * + * @param throwable the Throwable to examine, may be null + * @return an array of strings describing each stack frame, never null + */ + public static String[] getStackFrames(Throwable throwable) { + if (throwable == null) { + return ArrayUtils.EMPTY_STRING_ARRAY; + } + return getStackFrames(getStackTrace(throwable)); + } + + //----------------------------------------------------------------------- + /** + *

Returns an array where each element is a line from the argument.

+ * + *

The end of line is determined by the value of {@link SystemUtils#LINE_SEPARATOR}.

+ * + * @param stackTrace a stack trace String + * @return an array where each element is a line from the argument + */ + static String[] getStackFrames(String stackTrace) { + String linebreak = SystemUtils.LINE_SEPARATOR; + StringTokenizer frames = new StringTokenizer(stackTrace, linebreak); + List list = new ArrayList(); + while (frames.hasMoreTokens()) { + list.add(frames.nextToken()); + } + return list.toArray(new String[list.size()]); + } + + /** + *

Produces a List of stack frames - the message + * is not included. Only the trace of the specified exception is + * returned, any caused by trace is stripped.

+ * + *

This works in most cases - it will only fail if the exception + * message contains a line that starts with: + * "   at".

+ * + * @param t is any throwable + * @return List of stack frames + */ + static List getStackFrameList(Throwable t) { + String stackTrace = getStackTrace(t); + String linebreak = SystemUtils.LINE_SEPARATOR; + StringTokenizer frames = new StringTokenizer(stackTrace, linebreak); + List list = new ArrayList(); + boolean traceStarted = false; + while (frames.hasMoreTokens()) { + String token = frames.nextToken(); + // Determine if the line starts with at + int at = token.indexOf("at"); + if (at != -1 && token.substring(0, at).trim().isEmpty()) { + traceStarted = true; + list.add(token); + } else if (traceStarted) { + break; + } + } + return list; + } + + //----------------------------------------------------------------------- + /** + * Gets a short message summarising the exception. + *

+ * The message returned is of the form + * {ClassNameWithoutPackage}: {ThrowableMessage} + * + * @param th the throwable to get a message for, null returns empty string + * @return the message, non-null + * @since Commons Lang 2.2 + */ + public static String getMessage(Throwable th) { + if (th == null) { + return ""; + } + String clsName = ClassUtils.getShortClassName(th, null); + String msg = th.getMessage(); + return clsName + ": " + StringUtils.defaultString(msg); + } + + //----------------------------------------------------------------------- + /** + * Gets a short message summarising the root cause exception. + *

+ * The message returned is of the form + * {ClassNameWithoutPackage}: {ThrowableMessage} + * + * @param th the throwable to get a message for, null returns empty string + * @return the message, non-null + * @since Commons Lang 2.2 + */ + public static String getRootCauseMessage(Throwable th) { + Throwable root = ExceptionUtils.getRootCause(th); + root = root == null ? th : root; + return getMessage(root); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/exception/ExceptionUtilsTest.java b/ApacheCommonsLang/org/apache/commons/lang3/exception/ExceptionUtilsTest.java new file mode 100644 index 0000000..68dc6d2 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/exception/ExceptionUtilsTest.java @@ -0,0 +1,508 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.exception; + +import java.io.ByteArrayOutputStream; +import java.io.PrintStream; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.lang.reflect.Constructor; +import java.lang.reflect.Modifier; +import java.util.List; + +import junit.framework.TestCase; + +/** + * Tests {@link org.apache.commons.lang3.exception.ExceptionUtils}. + * + *

Notes

+ *

+ * Make sure this exception code does not depend on Java 1.4 nested exceptions. SVN revision 38990 does not compile with + * Java 1.3.1. + *

+ *
    + *
  • Compiled with Sun Java 1.3.1_15
  • + *
  • Tested with Sun Java 1.3.1_15
  • + *
  • Tested with Sun Java 1.4.2_12
  • + *
  • Tested with Sun Java 1.5.0_08
  • + *
  • All of the above on Windows XP SP2 + patches.
  • + *
+ *

+ * Gary Gregory; August 16, 2006. + *

+ * + * @since 1.0 + */ +public class ExceptionUtilsTest extends TestCase { + + private NestableException nested; + private Throwable withCause; + private Throwable withoutCause; + private Throwable jdkNoCause; + private ExceptionWithCause cyclicCause; + + public ExceptionUtilsTest(String name) { + super(name); + } + + @Override + public void setUp() { + withoutCause = createExceptionWithoutCause(); + nested = new NestableException(withoutCause); + withCause = new ExceptionWithCause(nested); + jdkNoCause = new NullPointerException(); + ExceptionWithCause a = new ExceptionWithCause(null); + ExceptionWithCause b = new ExceptionWithCause(a); + a.setCause(b); + cyclicCause = new ExceptionWithCause(a); + } + + @Override + protected void tearDown() throws Exception { + withoutCause = null; + nested = null; + withCause = null; + jdkNoCause = null; + cyclicCause = null; + } + + //----------------------------------------------------------------------- + private Throwable createExceptionWithoutCause() { + try { + throw new ExceptionWithoutCause(); + } catch (Throwable t) { + return t; + } + } + + private Throwable createExceptionWithCause() { + try { + try { + throw new ExceptionWithCause(createExceptionWithoutCause()); + } catch (Throwable t) { + throw new ExceptionWithCause(t); + } + } catch (Throwable t) { + return t; + } + } + + //----------------------------------------------------------------------- + + public void testConstructor() { + assertNotNull(new ExceptionUtils()); + Constructor[] cons = ExceptionUtils.class.getDeclaredConstructors(); + assertEquals(1, cons.length); + assertEquals(true, Modifier.isPublic(cons[0].getModifiers())); + assertEquals(true, Modifier.isPublic(ExceptionUtils.class.getModifiers())); + assertEquals(false, Modifier.isFinal(ExceptionUtils.class.getModifiers())); + } + + //----------------------------------------------------------------------- + @SuppressWarnings("deprecation") // Specifically tests the deprecated methods + public void testGetCause_Throwable() { + assertSame(null, ExceptionUtils.getCause(null)); + assertSame(null, ExceptionUtils.getCause(withoutCause)); + assertSame(withoutCause, ExceptionUtils.getCause(nested)); + assertSame(nested, ExceptionUtils.getCause(withCause)); + assertSame(null, ExceptionUtils.getCause(jdkNoCause)); + assertSame(cyclicCause.getCause(), ExceptionUtils.getCause(cyclicCause)); + assertSame(((ExceptionWithCause) cyclicCause.getCause()).getCause(), ExceptionUtils.getCause(cyclicCause.getCause())); + assertSame(cyclicCause.getCause(), ExceptionUtils.getCause(((ExceptionWithCause) cyclicCause.getCause()).getCause())); + } + + @SuppressWarnings("deprecation") // Specifically tests the deprecated methods + public void testGetCause_ThrowableArray() { + assertSame(null, ExceptionUtils.getCause(null, null)); + assertSame(null, ExceptionUtils.getCause(null, new String[0])); + + // not known type, so match on supplied method names + assertSame(nested, ExceptionUtils.getCause(withCause, null)); // default names + assertSame(null, ExceptionUtils.getCause(withCause, new String[0])); + assertSame(null, ExceptionUtils.getCause(withCause, new String[] {null})); + assertSame(nested, ExceptionUtils.getCause(withCause, new String[] {"getCause"})); + + // not known type, so match on supplied method names + assertSame(null, ExceptionUtils.getCause(withoutCause, null)); + assertSame(null, ExceptionUtils.getCause(withoutCause, new String[0])); + assertSame(null, ExceptionUtils.getCause(withoutCause, new String[] {null})); + assertSame(null, ExceptionUtils.getCause(withoutCause, new String[] {"getCause"})); + assertSame(null, ExceptionUtils.getCause(withoutCause, new String[] {"getTargetException"})); + } + + public void testGetRootCause_Throwable() { + assertSame(null, ExceptionUtils.getRootCause(null)); + assertSame(null, ExceptionUtils.getRootCause(withoutCause)); + assertSame(withoutCause, ExceptionUtils.getRootCause(nested)); + assertSame(withoutCause, ExceptionUtils.getRootCause(withCause)); + assertSame(null, ExceptionUtils.getRootCause(jdkNoCause)); + assertSame(((ExceptionWithCause) cyclicCause.getCause()).getCause(), ExceptionUtils.getRootCause(cyclicCause)); + } + + //----------------------------------------------------------------------- + public void testGetThrowableCount_Throwable() { + assertEquals(0, ExceptionUtils.getThrowableCount(null)); + assertEquals(1, ExceptionUtils.getThrowableCount(withoutCause)); + assertEquals(2, ExceptionUtils.getThrowableCount(nested)); + assertEquals(3, ExceptionUtils.getThrowableCount(withCause)); + assertEquals(1, ExceptionUtils.getThrowableCount(jdkNoCause)); + assertEquals(3, ExceptionUtils.getThrowableCount(cyclicCause)); + } + + //----------------------------------------------------------------------- + public void testGetThrowables_Throwable_null() { + assertEquals(0, ExceptionUtils.getThrowables(null).length); + } + + public void testGetThrowables_Throwable_withoutCause() { + Throwable[] throwables = ExceptionUtils.getThrowables(withoutCause); + assertEquals(1, throwables.length); + assertSame(withoutCause, throwables[0]); + } + + public void testGetThrowables_Throwable_nested() { + Throwable[] throwables = ExceptionUtils.getThrowables(nested); + assertEquals(2, throwables.length); + assertSame(nested, throwables[0]); + assertSame(withoutCause, throwables[1]); + } + + public void testGetThrowables_Throwable_withCause() { + Throwable[] throwables = ExceptionUtils.getThrowables(withCause); + assertEquals(3, throwables.length); + assertSame(withCause, throwables[0]); + assertSame(nested, throwables[1]); + assertSame(withoutCause, throwables[2]); + } + + public void testGetThrowables_Throwable_jdkNoCause() { + Throwable[] throwables = ExceptionUtils.getThrowables(jdkNoCause); + assertEquals(1, throwables.length); + assertSame(jdkNoCause, throwables[0]); + } + + public void testGetThrowables_Throwable_recursiveCause() { + Throwable[] throwables = ExceptionUtils.getThrowables(cyclicCause); + assertEquals(3, throwables.length); + assertSame(cyclicCause, throwables[0]); + assertSame(cyclicCause.getCause(), throwables[1]); + assertSame(((ExceptionWithCause) cyclicCause.getCause()).getCause(), throwables[2]); + } + + //----------------------------------------------------------------------- + public void testGetThrowableList_Throwable_null() { + List throwables = ExceptionUtils.getThrowableList(null); + assertEquals(0, throwables.size()); + } + + public void testGetThrowableList_Throwable_withoutCause() { + List throwables = ExceptionUtils.getThrowableList(withoutCause); + assertEquals(1, throwables.size()); + assertSame(withoutCause, throwables.get(0)); + } + + public void testGetThrowableList_Throwable_nested() { + List throwables = ExceptionUtils.getThrowableList(nested); + assertEquals(2, throwables.size()); + assertSame(nested, throwables.get(0)); + assertSame(withoutCause, throwables.get(1)); + } + + public void testGetThrowableList_Throwable_withCause() { + List throwables = ExceptionUtils.getThrowableList(withCause); + assertEquals(3, throwables.size()); + assertSame(withCause, throwables.get(0)); + assertSame(nested, throwables.get(1)); + assertSame(withoutCause, throwables.get(2)); + } + + public void testGetThrowableList_Throwable_jdkNoCause() { + List throwables = ExceptionUtils.getThrowableList(jdkNoCause); + assertEquals(1, throwables.size()); + assertSame(jdkNoCause, throwables.get(0)); + } + + public void testGetThrowableList_Throwable_recursiveCause() { + List throwables = ExceptionUtils.getThrowableList(cyclicCause); + assertEquals(3, throwables.size()); + assertSame(cyclicCause, throwables.get(0)); + assertSame(cyclicCause.getCause(), throwables.get(1)); + assertSame(((ExceptionWithCause) cyclicCause.getCause()).getCause(), throwables.get(2)); + } + + //----------------------------------------------------------------------- + public void testIndexOf_ThrowableClass() { + assertEquals(-1, ExceptionUtils.indexOfThrowable(null, null)); + assertEquals(-1, ExceptionUtils.indexOfThrowable(null, NestableException.class)); + + assertEquals(-1, ExceptionUtils.indexOfThrowable(withoutCause, null)); + assertEquals(-1, ExceptionUtils.indexOfThrowable(withoutCause, ExceptionWithCause.class)); + assertEquals(-1, ExceptionUtils.indexOfThrowable(withoutCause, NestableException.class)); + assertEquals(0, ExceptionUtils.indexOfThrowable(withoutCause, ExceptionWithoutCause.class)); + + assertEquals(-1, ExceptionUtils.indexOfThrowable(nested, null)); + assertEquals(-1, ExceptionUtils.indexOfThrowable(nested, ExceptionWithCause.class)); + assertEquals(0, ExceptionUtils.indexOfThrowable(nested, NestableException.class)); + assertEquals(1, ExceptionUtils.indexOfThrowable(nested, ExceptionWithoutCause.class)); + + assertEquals(-1, ExceptionUtils.indexOfThrowable(withCause, null)); + assertEquals(0, ExceptionUtils.indexOfThrowable(withCause, ExceptionWithCause.class)); + assertEquals(1, ExceptionUtils.indexOfThrowable(withCause, NestableException.class)); + assertEquals(2, ExceptionUtils.indexOfThrowable(withCause, ExceptionWithoutCause.class)); + + assertEquals(-1, ExceptionUtils.indexOfThrowable(withCause, Exception.class)); + } + + public void testIndexOf_ThrowableClassInt() { + assertEquals(-1, ExceptionUtils.indexOfThrowable(null, null, 0)); + assertEquals(-1, ExceptionUtils.indexOfThrowable(null, NestableException.class, 0)); + + assertEquals(-1, ExceptionUtils.indexOfThrowable(withoutCause, null)); + assertEquals(-1, ExceptionUtils.indexOfThrowable(withoutCause, ExceptionWithCause.class, 0)); + assertEquals(-1, ExceptionUtils.indexOfThrowable(withoutCause, NestableException.class, 0)); + assertEquals(0, ExceptionUtils.indexOfThrowable(withoutCause, ExceptionWithoutCause.class, 0)); + + assertEquals(-1, ExceptionUtils.indexOfThrowable(nested, null, 0)); + assertEquals(-1, ExceptionUtils.indexOfThrowable(nested, ExceptionWithCause.class, 0)); + assertEquals(0, ExceptionUtils.indexOfThrowable(nested, NestableException.class, 0)); + assertEquals(1, ExceptionUtils.indexOfThrowable(nested, ExceptionWithoutCause.class, 0)); + + assertEquals(-1, ExceptionUtils.indexOfThrowable(withCause, null)); + assertEquals(0, ExceptionUtils.indexOfThrowable(withCause, ExceptionWithCause.class, 0)); + assertEquals(1, ExceptionUtils.indexOfThrowable(withCause, NestableException.class, 0)); + assertEquals(2, ExceptionUtils.indexOfThrowable(withCause, ExceptionWithoutCause.class, 0)); + + assertEquals(0, ExceptionUtils.indexOfThrowable(withCause, ExceptionWithCause.class, -1)); + assertEquals(0, ExceptionUtils.indexOfThrowable(withCause, ExceptionWithCause.class, 0)); + assertEquals(-1, ExceptionUtils.indexOfThrowable(withCause, ExceptionWithCause.class, 1)); + assertEquals(-1, ExceptionUtils.indexOfThrowable(withCause, ExceptionWithCause.class, 9)); + + assertEquals(-1, ExceptionUtils.indexOfThrowable(withCause, Exception.class, 0)); + } + + //----------------------------------------------------------------------- + public void testIndexOfType_ThrowableClass() { + assertEquals(-1, ExceptionUtils.indexOfType(null, null)); + assertEquals(-1, ExceptionUtils.indexOfType(null, NestableException.class)); + + assertEquals(-1, ExceptionUtils.indexOfType(withoutCause, null)); + assertEquals(-1, ExceptionUtils.indexOfType(withoutCause, ExceptionWithCause.class)); + assertEquals(-1, ExceptionUtils.indexOfType(withoutCause, NestableException.class)); + assertEquals(0, ExceptionUtils.indexOfType(withoutCause, ExceptionWithoutCause.class)); + + assertEquals(-1, ExceptionUtils.indexOfType(nested, null)); + assertEquals(-1, ExceptionUtils.indexOfType(nested, ExceptionWithCause.class)); + assertEquals(0, ExceptionUtils.indexOfType(nested, NestableException.class)); + assertEquals(1, ExceptionUtils.indexOfType(nested, ExceptionWithoutCause.class)); + + assertEquals(-1, ExceptionUtils.indexOfType(withCause, null)); + assertEquals(0, ExceptionUtils.indexOfType(withCause, ExceptionWithCause.class)); + assertEquals(1, ExceptionUtils.indexOfType(withCause, NestableException.class)); + assertEquals(2, ExceptionUtils.indexOfType(withCause, ExceptionWithoutCause.class)); + + assertEquals(0, ExceptionUtils.indexOfType(withCause, Exception.class)); + } + + public void testIndexOfType_ThrowableClassInt() { + assertEquals(-1, ExceptionUtils.indexOfType(null, null, 0)); + assertEquals(-1, ExceptionUtils.indexOfType(null, NestableException.class, 0)); + + assertEquals(-1, ExceptionUtils.indexOfType(withoutCause, null)); + assertEquals(-1, ExceptionUtils.indexOfType(withoutCause, ExceptionWithCause.class, 0)); + assertEquals(-1, ExceptionUtils.indexOfType(withoutCause, NestableException.class, 0)); + assertEquals(0, ExceptionUtils.indexOfType(withoutCause, ExceptionWithoutCause.class, 0)); + + assertEquals(-1, ExceptionUtils.indexOfType(nested, null, 0)); + assertEquals(-1, ExceptionUtils.indexOfType(nested, ExceptionWithCause.class, 0)); + assertEquals(0, ExceptionUtils.indexOfType(nested, NestableException.class, 0)); + assertEquals(1, ExceptionUtils.indexOfType(nested, ExceptionWithoutCause.class, 0)); + + assertEquals(-1, ExceptionUtils.indexOfType(withCause, null)); + assertEquals(0, ExceptionUtils.indexOfType(withCause, ExceptionWithCause.class, 0)); + assertEquals(1, ExceptionUtils.indexOfType(withCause, NestableException.class, 0)); + assertEquals(2, ExceptionUtils.indexOfType(withCause, ExceptionWithoutCause.class, 0)); + + assertEquals(0, ExceptionUtils.indexOfType(withCause, ExceptionWithCause.class, -1)); + assertEquals(0, ExceptionUtils.indexOfType(withCause, ExceptionWithCause.class, 0)); + assertEquals(-1, ExceptionUtils.indexOfType(withCause, ExceptionWithCause.class, 1)); + assertEquals(-1, ExceptionUtils.indexOfType(withCause, ExceptionWithCause.class, 9)); + + assertEquals(0, ExceptionUtils.indexOfType(withCause, Exception.class, 0)); + } + + //----------------------------------------------------------------------- + public void testPrintRootCauseStackTrace_Throwable() throws Exception { + ExceptionUtils.printRootCauseStackTrace(null); + // could pipe system.err to a known stream, but not much point as + // internally this method calls stram method anyway + } + + public void testPrintRootCauseStackTrace_ThrowableStream() throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(1024); + ExceptionUtils.printRootCauseStackTrace(null, (PrintStream) null); + ExceptionUtils.printRootCauseStackTrace(null, new PrintStream(out)); + assertEquals(0, out.toString().length()); + + out = new ByteArrayOutputStream(1024); + try { + ExceptionUtils.printRootCauseStackTrace(withCause, (PrintStream) null); + fail(); + } catch (IllegalArgumentException ex) { + } + + out = new ByteArrayOutputStream(1024); + Throwable withCause = createExceptionWithCause(); + ExceptionUtils.printRootCauseStackTrace(withCause, new PrintStream(out)); + String stackTrace = out.toString(); + assertTrue(stackTrace.indexOf(ExceptionUtils.WRAPPED_MARKER) != -1); + + out = new ByteArrayOutputStream(1024); + ExceptionUtils.printRootCauseStackTrace(withoutCause, new PrintStream(out)); + stackTrace = out.toString(); + assertTrue(stackTrace.indexOf(ExceptionUtils.WRAPPED_MARKER) == -1); + } + + public void testPrintRootCauseStackTrace_ThrowableWriter() throws Exception { + StringWriter writer = new StringWriter(1024); + ExceptionUtils.printRootCauseStackTrace(null, (PrintWriter) null); + ExceptionUtils.printRootCauseStackTrace(null, new PrintWriter(writer)); + assertEquals(0, writer.getBuffer().length()); + + writer = new StringWriter(1024); + try { + ExceptionUtils.printRootCauseStackTrace(withCause, (PrintWriter) null); + fail(); + } catch (IllegalArgumentException ex) { + } + + writer = new StringWriter(1024); + Throwable withCause = createExceptionWithCause(); + ExceptionUtils.printRootCauseStackTrace(withCause, new PrintWriter(writer)); + String stackTrace = writer.toString(); + assertTrue(stackTrace.indexOf(ExceptionUtils.WRAPPED_MARKER) != -1); + + writer = new StringWriter(1024); + ExceptionUtils.printRootCauseStackTrace(withoutCause, new PrintWriter(writer)); + stackTrace = writer.toString(); + assertTrue(stackTrace.indexOf(ExceptionUtils.WRAPPED_MARKER) == -1); + } + + //----------------------------------------------------------------------- + public void testGetRootCauseStackTrace_Throwable() throws Exception { + assertEquals(0, ExceptionUtils.getRootCauseStackTrace(null).length); + + Throwable withCause = createExceptionWithCause(); + String[] stackTrace = ExceptionUtils.getRootCauseStackTrace(withCause); + boolean match = false; + for (String element : stackTrace) { + if (element.startsWith(ExceptionUtils.WRAPPED_MARKER)) { + match = true; + break; + } + } + assertEquals(true, match); + + stackTrace = ExceptionUtils.getRootCauseStackTrace(withoutCause); + match = false; + for (String element : stackTrace) { + if (element.startsWith(ExceptionUtils.WRAPPED_MARKER)) { + match = true; + break; + } + } + assertEquals(false, match); + } + + public void testRemoveCommonFrames_ListList() throws Exception { + try { + ExceptionUtils.removeCommonFrames(null, null); + fail(); + } catch (IllegalArgumentException ex) { + } + } + + public void test_getMessage_Throwable() { + Throwable th = null; + assertEquals("", ExceptionUtils.getMessage(th)); + + th = new IllegalArgumentException("Base"); + assertEquals("IllegalArgumentException: Base", ExceptionUtils.getMessage(th)); + + th = new ExceptionWithCause("Wrapper", th); + assertEquals("ExceptionUtilsTest.ExceptionWithCause: Wrapper", ExceptionUtils.getMessage(th)); + } + + public void test_getRootCauseMessage_Throwable() { + Throwable th = null; + assertEquals("", ExceptionUtils.getRootCauseMessage(th)); + + th = new IllegalArgumentException("Base"); + assertEquals("IllegalArgumentException: Base", ExceptionUtils.getRootCauseMessage(th)); + + th = new ExceptionWithCause("Wrapper", th); + assertEquals("IllegalArgumentException: Base", ExceptionUtils.getRootCauseMessage(th)); + } + + //----------------------------------------------------------------------- + /** + * Provides a method with a well known chained/nested exception + * name which matches the full signature (e.g. has a return value + * of Throwable. + */ + private static class ExceptionWithCause extends Exception { + private Throwable cause; + + public ExceptionWithCause(String str, Throwable cause) { + super(str); + setCause(cause); + } + + public ExceptionWithCause(Throwable cause) { + super(); + setCause(cause); + } + + @Override + public Throwable getCause() { + return cause; + } + + public void setCause(Throwable cause) { + this.cause = cause; + } + } + + /** + * Provides a method with a well known chained/nested exception + * name which does not match the full signature (e.g. lacks a + * return value of Throwable. + */ + private static class ExceptionWithoutCause extends Exception { + @SuppressWarnings("unused") + public void getTargetException() { + } + } + + // Temporary classes to allow the nested exception code to be removed + // prior to a rewrite of this test class. + private static class NestableException extends Exception { + @SuppressWarnings("unused") + public NestableException() { super(); } + public NestableException(Throwable t) { super(t); } + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/exception/package.html b/ApacheCommonsLang/org/apache/commons/lang3/exception/package.html new file mode 100644 index 0000000..15222ea --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/exception/package.html @@ -0,0 +1,27 @@ + + + +Provides functionality for Exceptions. +

Contains the concept of an exception with context i.e. such an exception +will contain a map with keys and values. This provides an easy way to pass valuable +state information at exception time in useful form to a calling process.

+

Lastly, {@link org.apache.commons.lang3.exception.ExceptionUtils} +also contains Throwable manipulation and examination routines.

+@since 1.0 + + diff --git a/ApacheCommonsLang/org/apache/commons/lang3/math/Fraction.java b/ApacheCommonsLang/org/apache/commons/lang3/math/Fraction.java new file mode 100644 index 0000000..14afc15 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/math/Fraction.java @@ -0,0 +1,960 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.math; + +import java.math.BigInteger; + +/** + *

Fraction is a Number implementation that + * stores fractions accurately.

+ * + *

This class is immutable, and interoperable with most methods that accept + * a Number.

+ * + *

Note that this class is intended for common use cases, it is int + * based and thus suffers from various overflow issues. For a BigInteger based + * equivalent, please see the Commons Math BigFraction class.

+ * + * @since 2.0 + * @version $Id: Fraction.java 1309977 2012-04-05 17:53:39Z ggregory $ + */ +public final class Fraction extends Number implements Comparable { + + /** + * Required for serialization support. Lang version 2.0. + * + * @see java.io.Serializable + */ + private static final long serialVersionUID = 65382027393090L; + + /** + * Fraction representation of 0. + */ + public static final Fraction ZERO = new Fraction(0, 1); + /** + * Fraction representation of 1. + */ + public static final Fraction ONE = new Fraction(1, 1); + /** + * Fraction representation of 1/2. + */ + public static final Fraction ONE_HALF = new Fraction(1, 2); + /** + * Fraction representation of 1/3. + */ + public static final Fraction ONE_THIRD = new Fraction(1, 3); + /** + * Fraction representation of 2/3. + */ + public static final Fraction TWO_THIRDS = new Fraction(2, 3); + /** + * Fraction representation of 1/4. + */ + public static final Fraction ONE_QUARTER = new Fraction(1, 4); + /** + * Fraction representation of 2/4. + */ + public static final Fraction TWO_QUARTERS = new Fraction(2, 4); + /** + * Fraction representation of 3/4. + */ + public static final Fraction THREE_QUARTERS = new Fraction(3, 4); + /** + * Fraction representation of 1/5. + */ + public static final Fraction ONE_FIFTH = new Fraction(1, 5); + /** + * Fraction representation of 2/5. + */ + public static final Fraction TWO_FIFTHS = new Fraction(2, 5); + /** + * Fraction representation of 3/5. + */ + public static final Fraction THREE_FIFTHS = new Fraction(3, 5); + /** + * Fraction representation of 4/5. + */ + public static final Fraction FOUR_FIFTHS = new Fraction(4, 5); + + + /** + * The numerator number part of the fraction (the three in three sevenths). + */ + private final int numerator; + /** + * The denominator number part of the fraction (the seven in three sevenths). + */ + private final int denominator; + + /** + * Cached output hashCode (class is immutable). + */ + private transient int hashCode = 0; + /** + * Cached output toString (class is immutable). + */ + private transient String toString = null; + /** + * Cached output toProperString (class is immutable). + */ + private transient String toProperString = null; + + /** + *

Constructs a Fraction instance with the 2 parts + * of a fraction Y/Z.

+ * + * @param numerator the numerator, for example the three in 'three sevenths' + * @param denominator the denominator, for example the seven in 'three sevenths' + */ + private Fraction(int numerator, int denominator) { + super(); + this.numerator = numerator; + this.denominator = denominator; + } + + /** + *

Creates a Fraction instance with the 2 parts + * of a fraction Y/Z.

+ * + *

Any negative signs are resolved to be on the numerator.

+ * + * @param numerator the numerator, for example the three in 'three sevenths' + * @param denominator the denominator, for example the seven in 'three sevenths' + * @return a new fraction instance + * @throws ArithmeticException if the denominator is zero + * or the denominator is {@code negative} and the numerator is {@code Integer#MIN_VALUE} + */ + public static Fraction getFraction(int numerator, int denominator) { + if (denominator == 0) { + throw new ArithmeticException("The denominator must not be zero"); + } + if (denominator < 0) { + if (numerator==Integer.MIN_VALUE || + denominator==Integer.MIN_VALUE) { + throw new ArithmeticException("overflow: can't negate"); + } + numerator = -numerator; + denominator = -denominator; + } + return new Fraction(numerator, denominator); + } + + /** + *

Creates a Fraction instance with the 3 parts + * of a fraction X Y/Z.

+ * + *

The negative sign must be passed in on the whole number part.

+ * + * @param whole the whole number, for example the one in 'one and three sevenths' + * @param numerator the numerator, for example the three in 'one and three sevenths' + * @param denominator the denominator, for example the seven in 'one and three sevenths' + * @return a new fraction instance + * @throws ArithmeticException if the denominator is zero + * @throws ArithmeticException if the denominator is negative + * @throws ArithmeticException if the numerator is negative + * @throws ArithmeticException if the resulting numerator exceeds + * Integer.MAX_VALUE + */ + public static Fraction getFraction(int whole, int numerator, int denominator) { + if (denominator == 0) { + throw new ArithmeticException("The denominator must not be zero"); + } + if (denominator < 0) { + throw new ArithmeticException("The denominator must not be negative"); + } + if (numerator < 0) { + throw new ArithmeticException("The numerator must not be negative"); + } + long numeratorValue; + if (whole < 0) { + numeratorValue = whole * (long)denominator - numerator; + } else { + numeratorValue = whole * (long)denominator + numerator; + } + if (numeratorValue < Integer.MIN_VALUE || + numeratorValue > Integer.MAX_VALUE) { + throw new ArithmeticException("Numerator too large to represent as an Integer."); + } + return new Fraction((int) numeratorValue, denominator); + } + + /** + *

Creates a reduced Fraction instance with the 2 parts + * of a fraction Y/Z.

+ * + *

For example, if the input parameters represent 2/4, then the created + * fraction will be 1/2.

+ * + *

Any negative signs are resolved to be on the numerator.

+ * + * @param numerator the numerator, for example the three in 'three sevenths' + * @param denominator the denominator, for example the seven in 'three sevenths' + * @return a new fraction instance, with the numerator and denominator reduced + * @throws ArithmeticException if the denominator is zero + */ + public static Fraction getReducedFraction(int numerator, int denominator) { + if (denominator == 0) { + throw new ArithmeticException("The denominator must not be zero"); + } + if (numerator==0) { + return ZERO; // normalize zero. + } + // allow 2^k/-2^31 as a valid fraction (where k>0) + if (denominator==Integer.MIN_VALUE && (numerator&1)==0) { + numerator/=2; denominator/=2; + } + if (denominator < 0) { + if (numerator==Integer.MIN_VALUE || + denominator==Integer.MIN_VALUE) { + throw new ArithmeticException("overflow: can't negate"); + } + numerator = -numerator; + denominator = -denominator; + } + // simplify fraction. + int gcd = greatestCommonDivisor(numerator, denominator); + numerator /= gcd; + denominator /= gcd; + return new Fraction(numerator, denominator); + } + + /** + *

Creates a Fraction instance from a double value.

+ * + *

This method uses the + * continued fraction algorithm, computing a maximum of + * 25 convergents and bounding the denominator by 10,000.

+ * + * @param value the double value to convert + * @return a new fraction instance that is close to the value + * @throws ArithmeticException if |value| > Integer.MAX_VALUE + * or value = NaN + * @throws ArithmeticException if the calculated denominator is zero + * @throws ArithmeticException if the the algorithm does not converge + */ + public static Fraction getFraction(double value) { + int sign = value < 0 ? -1 : 1; + value = Math.abs(value); + if (value > Integer.MAX_VALUE || Double.isNaN(value)) { + throw new ArithmeticException + ("The value must not be greater than Integer.MAX_VALUE or NaN"); + } + int wholeNumber = (int) value; + value -= wholeNumber; + + int numer0 = 0; // the pre-previous + int denom0 = 1; // the pre-previous + int numer1 = 1; // the previous + int denom1 = 0; // the previous + int numer2 = 0; // the current, setup in calculation + int denom2 = 0; // the current, setup in calculation + int a1 = (int) value; + int a2 = 0; + double x1 = 1; + double x2 = 0; + double y1 = value - a1; + double y2 = 0; + double delta1, delta2 = Double.MAX_VALUE; + double fraction; + int i = 1; +// System.out.println("---"); + do { + delta1 = delta2; + a2 = (int) (x1 / y1); + x2 = y1; + y2 = x1 - a2 * y1; + numer2 = a1 * numer1 + numer0; + denom2 = a1 * denom1 + denom0; + fraction = (double) numer2 / (double) denom2; + delta2 = Math.abs(value - fraction); +// System.out.println(numer2 + " " + denom2 + " " + fraction + " " + delta2 + " " + y1); + a1 = a2; + x1 = x2; + y1 = y2; + numer0 = numer1; + denom0 = denom1; + numer1 = numer2; + denom1 = denom2; + i++; +// System.out.println(">>" + delta1 +" "+ delta2+" "+(delta1 > delta2)+" "+i+" "+denom2); + } while (delta1 > delta2 && denom2 <= 10000 && denom2 > 0 && i < 25); + if (i == 25) { + throw new ArithmeticException("Unable to convert double to fraction"); + } + return getReducedFraction((numer0 + wholeNumber * denom0) * sign, denom0); + } + + /** + *

Creates a Fraction from a String.

+ * + *

The formats accepted are:

+ * + *
    + *
  1. double String containing a dot
  2. + *
  3. 'X Y/Z'
  4. + *
  5. 'Y/Z'
  6. + *
  7. 'X' (a simple whole number)
  8. + *
+ * and a .

+ * + * @param str the string to parse, must not be null + * @return the new Fraction instance + * @throws IllegalArgumentException if the string is null + * @throws NumberFormatException if the number format is invalid + */ + public static Fraction getFraction(String str) { + if (str == null) { + throw new IllegalArgumentException("The string must not be null"); + } + // parse double format + int pos = str.indexOf('.'); + if (pos >= 0) { + return getFraction(Double.parseDouble(str)); + } + + // parse X Y/Z format + pos = str.indexOf(' '); + if (pos > 0) { + int whole = Integer.parseInt(str.substring(0, pos)); + str = str.substring(pos + 1); + pos = str.indexOf('/'); + if (pos < 0) { + throw new NumberFormatException("The fraction could not be parsed as the format X Y/Z"); + } else { + int numer = Integer.parseInt(str.substring(0, pos)); + int denom = Integer.parseInt(str.substring(pos + 1)); + return getFraction(whole, numer, denom); + } + } + + // parse Y/Z format + pos = str.indexOf('/'); + if (pos < 0) { + // simple whole number + return getFraction(Integer.parseInt(str), 1); + } else { + int numer = Integer.parseInt(str.substring(0, pos)); + int denom = Integer.parseInt(str.substring(pos + 1)); + return getFraction(numer, denom); + } + } + + // Accessors + //------------------------------------------------------------------- + + /** + *

Gets the numerator part of the fraction.

+ * + *

This method may return a value greater than the denominator, an + * improper fraction, such as the seven in 7/4.

+ * + * @return the numerator fraction part + */ + public int getNumerator() { + return numerator; + } + + /** + *

Gets the denominator part of the fraction.

+ * + * @return the denominator fraction part + */ + public int getDenominator() { + return denominator; + } + + /** + *

Gets the proper numerator, always positive.

+ * + *

An improper fraction 7/4 can be resolved into a proper one, 1 3/4. + * This method returns the 3 from the proper fraction.

+ * + *

If the fraction is negative such as -7/4, it can be resolved into + * -1 3/4, so this method returns the positive proper numerator, 3.

+ * + * @return the numerator fraction part of a proper fraction, always positive + */ + public int getProperNumerator() { + return Math.abs(numerator % denominator); + } + + /** + *

Gets the proper whole part of the fraction.

+ * + *

An improper fraction 7/4 can be resolved into a proper one, 1 3/4. + * This method returns the 1 from the proper fraction.

+ * + *

If the fraction is negative such as -7/4, it can be resolved into + * -1 3/4, so this method returns the positive whole part -1.

+ * + * @return the whole fraction part of a proper fraction, that includes the sign + */ + public int getProperWhole() { + return numerator / denominator; + } + + // Number methods + //------------------------------------------------------------------- + + /** + *

Gets the fraction as an int. This returns the whole number + * part of the fraction.

+ * + * @return the whole number fraction part + */ + @Override + public int intValue() { + return numerator / denominator; + } + + /** + *

Gets the fraction as a long. This returns the whole number + * part of the fraction.

+ * + * @return the whole number fraction part + */ + @Override + public long longValue() { + return (long) numerator / denominator; + } + + /** + *

Gets the fraction as a float. This calculates the fraction + * as the numerator divided by denominator.

+ * + * @return the fraction as a float + */ + @Override + public float floatValue() { + return (float) numerator / (float) denominator; + } + + /** + *

Gets the fraction as a double. This calculates the fraction + * as the numerator divided by denominator.

+ * + * @return the fraction as a double + */ + @Override + public double doubleValue() { + return (double) numerator / (double) denominator; + } + + // Calculations + //------------------------------------------------------------------- + + /** + *

Reduce the fraction to the smallest values for the numerator and + * denominator, returning the result.

+ * + *

For example, if this fraction represents 2/4, then the result + * will be 1/2.

+ * + * @return a new reduced fraction instance, or this if no simplification possible + */ + public Fraction reduce() { + if (numerator == 0) { + return equals(ZERO) ? this : ZERO; + } + int gcd = greatestCommonDivisor(Math.abs(numerator), denominator); + if (gcd == 1) { + return this; + } + return Fraction.getFraction(numerator / gcd, denominator / gcd); + } + + /** + *

Gets a fraction that is the inverse (1/fraction) of this one.

+ * + *

The returned fraction is not reduced.

+ * + * @return a new fraction instance with the numerator and denominator + * inverted. + * @throws ArithmeticException if the fraction represents zero. + */ + public Fraction invert() { + if (numerator == 0) { + throw new ArithmeticException("Unable to invert zero."); + } + if (numerator==Integer.MIN_VALUE) { + throw new ArithmeticException("overflow: can't negate numerator"); + } + if (numerator<0) { + return new Fraction(-denominator, -numerator); + } else { + return new Fraction(denominator, numerator); + } + } + + /** + *

Gets a fraction that is the negative (-fraction) of this one.

+ * + *

The returned fraction is not reduced.

+ * + * @return a new fraction instance with the opposite signed numerator + */ + public Fraction negate() { + // the positive range is one smaller than the negative range of an int. + if (numerator==Integer.MIN_VALUE) { + throw new ArithmeticException("overflow: too large to negate"); + } + return new Fraction(-numerator, denominator); + } + + /** + *

Gets a fraction that is the positive equivalent of this one.

+ *

More precisely: (fraction >= 0 ? this : -fraction)

+ * + *

The returned fraction is not reduced.

+ * + * @return this if it is positive, or a new positive fraction + * instance with the opposite signed numerator + */ + public Fraction abs() { + if (numerator >= 0) { + return this; + } + return negate(); + } + + /** + *

Gets a fraction that is raised to the passed in power.

+ * + *

The returned fraction is in reduced form.

+ * + * @param power the power to raise the fraction to + * @return this if the power is one, ONE if the power + * is zero (even if the fraction equals ZERO) or a new fraction instance + * raised to the appropriate power + * @throws ArithmeticException if the resulting numerator or denominator exceeds + * Integer.MAX_VALUE + */ + public Fraction pow(int power) { + if (power == 1) { + return this; + } else if (power == 0) { + return ONE; + } else if (power < 0) { + if (power==Integer.MIN_VALUE) { // MIN_VALUE can't be negated. + return this.invert().pow(2).pow(-(power/2)); + } + return this.invert().pow(-power); + } else { + Fraction f = this.multiplyBy(this); + if (power % 2 == 0) { // if even... + return f.pow(power/2); + } else { // if odd... + return f.pow(power/2).multiplyBy(this); + } + } + } + + /** + *

Gets the greatest common divisor of the absolute value of + * two numbers, using the "binary gcd" method which avoids + * division and modulo operations. See Knuth 4.5.2 algorithm B. + * This algorithm is due to Josef Stein (1961).

+ * + * @param u a non-zero number + * @param v a non-zero number + * @return the greatest common divisor, never zero + */ + private static int greatestCommonDivisor(int u, int v) { + // From Commons Math: + if (u == 0 || v == 0) { + if (u == Integer.MIN_VALUE || v == Integer.MIN_VALUE) { + throw new ArithmeticException("overflow: gcd is 2^31"); + } + return Math.abs(u) + Math.abs(v); + } + //if either operand is abs 1, return 1: + if (Math.abs(u) == 1 || Math.abs(v) == 1) { + return 1; + } + // keep u and v negative, as negative integers range down to + // -2^31, while positive numbers can only be as large as 2^31-1 + // (i.e. we can't necessarily negate a negative number without + // overflow) + if (u>0) { u=-u; } // make u negative + if (v>0) { v=-v; } // make v negative + // B1. [Find power of 2] + int k=0; + while ((u&1)==0 && (v&1)==0 && k<31) { // while u and v are both even... + u/=2; v/=2; k++; // cast out twos. + } + if (k==31) { + throw new ArithmeticException("overflow: gcd is 2^31"); + } + // B2. Initialize: u and v have been divided by 2^k and at least + // one is odd. + int t = (u&1)==1 ? v : -(u/2)/*B3*/; + // t negative: u was odd, v may be even (t replaces v) + // t positive: u was even, v is odd (t replaces u) + do { + /* assert u<0 && v<0; */ + // B4/B3: cast out twos from t. + while ((t&1)==0) { // while t is even.. + t/=2; // cast out twos + } + // B5 [reset max(u,v)] + if (t>0) { + u = -t; + } else { + v = t; + } + // B6/B3. at this point both u and v should be odd. + t = (v - u)/2; + // |u| larger: t positive (replace u) + // |v| larger: t negative (replace v) + } while (t!=0); + return -u*(1<x*y + * @throws ArithmeticException if the result can not be represented as + * an int + */ + private static int mulAndCheck(int x, int y) { + long m = (long)x*(long)y; + if (m < Integer.MIN_VALUE || + m > Integer.MAX_VALUE) { + throw new ArithmeticException("overflow: mul"); + } + return (int)m; + } + + /** + * Multiply two non-negative integers, checking for overflow. + * + * @param x a non-negative factor + * @param y a non-negative factor + * @return the product x*y + * @throws ArithmeticException if the result can not be represented as + * an int + */ + private static int mulPosAndCheck(int x, int y) { + /* assert x>=0 && y>=0; */ + long m = (long)x*(long)y; + if (m > Integer.MAX_VALUE) { + throw new ArithmeticException("overflow: mulPos"); + } + return (int)m; + } + + /** + * Add two integers, checking for overflow. + * + * @param x an addend + * @param y an addend + * @return the sum x+y + * @throws ArithmeticException if the result can not be represented as + * an int + */ + private static int addAndCheck(int x, int y) { + long s = (long)x+(long)y; + if (s < Integer.MIN_VALUE || + s > Integer.MAX_VALUE) { + throw new ArithmeticException("overflow: add"); + } + return (int)s; + } + + /** + * Subtract two integers, checking for overflow. + * + * @param x the minuend + * @param y the subtrahend + * @return the difference x-y + * @throws ArithmeticException if the result can not be represented as + * an int + */ + private static int subAndCheck(int x, int y) { + long s = (long)x-(long)y; + if (s < Integer.MIN_VALUE || + s > Integer.MAX_VALUE) { + throw new ArithmeticException("overflow: add"); + } + return (int)s; + } + + /** + *

Adds the value of this fraction to another, returning the result in reduced form. + * The algorithm follows Knuth, 4.5.1.

+ * + * @param fraction the fraction to add, must not be null + * @return a Fraction instance with the resulting values + * @throws IllegalArgumentException if the fraction is null + * @throws ArithmeticException if the resulting numerator or denominator exceeds + * Integer.MAX_VALUE + */ + public Fraction add(Fraction fraction) { + return addSub(fraction, true /* add */); + } + + /** + *

Subtracts the value of another fraction from the value of this one, + * returning the result in reduced form.

+ * + * @param fraction the fraction to subtract, must not be null + * @return a Fraction instance with the resulting values + * @throws IllegalArgumentException if the fraction is null + * @throws ArithmeticException if the resulting numerator or denominator + * cannot be represented in an int. + */ + public Fraction subtract(Fraction fraction) { + return addSub(fraction, false /* subtract */); + } + + /** + * Implement add and subtract using algorithm described in Knuth 4.5.1. + * + * @param fraction the fraction to subtract, must not be null + * @param isAdd true to add, false to subtract + * @return a Fraction instance with the resulting values + * @throws IllegalArgumentException if the fraction is null + * @throws ArithmeticException if the resulting numerator or denominator + * cannot be represented in an int. + */ + private Fraction addSub(Fraction fraction, boolean isAdd) { + if (fraction == null) { + throw new IllegalArgumentException("The fraction must not be null"); + } + // zero is identity for addition. + if (numerator == 0) { + return isAdd ? fraction : fraction.negate(); + } + if (fraction.numerator == 0) { + return this; + } + // if denominators are randomly distributed, d1 will be 1 about 61% + // of the time. + int d1 = greatestCommonDivisor(denominator, fraction.denominator); + if (d1==1) { + // result is ( (u*v' +/- u'v) / u'v') + int uvp = mulAndCheck(numerator, fraction.denominator); + int upv = mulAndCheck(fraction.numerator, denominator); + return new Fraction + (isAdd ? addAndCheck(uvp, upv) : subAndCheck(uvp, upv), + mulPosAndCheck(denominator, fraction.denominator)); + } + // the quantity 't' requires 65 bits of precision; see knuth 4.5.1 + // exercise 7. we're going to use a BigInteger. + // t = u(v'/d1) +/- v(u'/d1) + BigInteger uvp = BigInteger.valueOf(numerator) + .multiply(BigInteger.valueOf(fraction.denominator/d1)); + BigInteger upv = BigInteger.valueOf(fraction.numerator) + .multiply(BigInteger.valueOf(denominator/d1)); + BigInteger t = isAdd ? uvp.add(upv) : uvp.subtract(upv); + // but d2 doesn't need extra precision because + // d2 = gcd(t,d1) = gcd(t mod d1, d1) + int tmodd1 = t.mod(BigInteger.valueOf(d1)).intValue(); + int d2 = tmodd1==0?d1:greatestCommonDivisor(tmodd1, d1); + + // result is (t/d2) / (u'/d1)(v'/d2) + BigInteger w = t.divide(BigInteger.valueOf(d2)); + if (w.bitLength() > 31) { + throw new ArithmeticException + ("overflow: numerator too large after multiply"); + } + return new Fraction + (w.intValue(), + mulPosAndCheck(denominator/d1, fraction.denominator/d2)); + } + + /** + *

Multiplies the value of this fraction by another, returning the + * result in reduced form.

+ * + * @param fraction the fraction to multiply by, must not be null + * @return a Fraction instance with the resulting values + * @throws IllegalArgumentException if the fraction is null + * @throws ArithmeticException if the resulting numerator or denominator exceeds + * Integer.MAX_VALUE + */ + public Fraction multiplyBy(Fraction fraction) { + if (fraction == null) { + throw new IllegalArgumentException("The fraction must not be null"); + } + if (numerator == 0 || fraction.numerator == 0) { + return ZERO; + } + // knuth 4.5.1 + // make sure we don't overflow unless the result *must* overflow. + int d1 = greatestCommonDivisor(numerator, fraction.denominator); + int d2 = greatestCommonDivisor(fraction.numerator, denominator); + return getReducedFraction + (mulAndCheck(numerator/d1, fraction.numerator/d2), + mulPosAndCheck(denominator/d2, fraction.denominator/d1)); + } + + /** + *

Divide the value of this fraction by another.

+ * + * @param fraction the fraction to divide by, must not be null + * @return a Fraction instance with the resulting values + * @throws IllegalArgumentException if the fraction is null + * @throws ArithmeticException if the fraction to divide by is zero + * @throws ArithmeticException if the resulting numerator or denominator exceeds + * Integer.MAX_VALUE + */ + public Fraction divideBy(Fraction fraction) { + if (fraction == null) { + throw new IllegalArgumentException("The fraction must not be null"); + } + if (fraction.numerator == 0) { + throw new ArithmeticException("The fraction to divide by must not be zero"); + } + return multiplyBy(fraction.invert()); + } + + // Basics + //------------------------------------------------------------------- + + /** + *

Compares this fraction to another object to test if they are equal.

. + * + *

To be equal, both values must be equal. Thus 2/4 is not equal to 1/2.

+ * + * @param obj the reference object with which to compare + * @return true if this object is equal + */ + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj instanceof Fraction == false) { + return false; + } + Fraction other = (Fraction) obj; + return getNumerator() == other.getNumerator() && + getDenominator() == other.getDenominator(); + } + + /** + *

Gets a hashCode for the fraction.

+ * + * @return a hash code value for this object + */ + @Override + public int hashCode() { + if (hashCode == 0) { + // hashcode update should be atomic. + hashCode = 37 * (37 * 17 + getNumerator()) + getDenominator(); + } + return hashCode; + } + + /** + *

Compares this object to another based on size.

+ * + *

Note: this class has a natural ordering that is inconsistent + * with equals, because, for example, equals treats 1/2 and 2/4 as + * different, whereas compareTo treats them as equal. + * + * @param other the object to compare to + * @return -1 if this is less, 0 if equal, +1 if greater + * @throws ClassCastException if the object is not a Fraction + * @throws NullPointerException if the object is null + */ + @Override + public int compareTo(Fraction other) { + if (this==other) { + return 0; + } + if (numerator == other.numerator && denominator == other.denominator) { + return 0; + } + + // otherwise see which is less + long first = (long) numerator * (long) other.denominator; + long second = (long) other.numerator * (long) denominator; + if (first == second) { + return 0; + } else if (first < second) { + return -1; + } else { + return 1; + } + } + + /** + *

Gets the fraction as a String.

+ * + *

The format used is 'numerator/denominator' always. + * + * @return a String form of the fraction + */ + @Override + public String toString() { + if (toString == null) { + toString = new StringBuilder(32) + .append(getNumerator()) + .append('/') + .append(getDenominator()).toString(); + } + return toString; + } + + /** + *

Gets the fraction as a proper String in the format X Y/Z.

+ * + *

The format used in 'wholeNumber numerator/denominator'. + * If the whole number is zero it will be ommitted. If the numerator is zero, + * only the whole number is returned.

+ * + * @return a String form of the fraction + */ + public String toProperString() { + if (toProperString == null) { + if (numerator == 0) { + toProperString = "0"; + } else if (numerator == denominator) { + toProperString = "1"; + } else if (numerator == -1 * denominator) { + toProperString = "-1"; + } else if ((numerator>0?-numerator:numerator) < -denominator) { + // note that we do the magnitude comparison test above with + // NEGATIVE (not positive) numbers, since negative numbers + // have a larger range. otherwise numerator==Integer.MIN_VALUE + // is handled incorrectly. + int properNumerator = getProperNumerator(); + if (properNumerator == 0) { + toProperString = Integer.toString(getProperWhole()); + } else { + toProperString = new StringBuilder(32) + .append(getProperWhole()).append(' ') + .append(properNumerator).append('/') + .append(getDenominator()).toString(); + } + } else { + toProperString = new StringBuilder(32) + .append(getNumerator()).append('/') + .append(getDenominator()).toString(); + } + } + return toProperString; + } +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/math/FractionTest.java b/ApacheCommonsLang/org/apache/commons/lang3/math/FractionTest.java new file mode 100644 index 0000000..45e56c2 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/math/FractionTest.java @@ -0,0 +1,1346 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.commons.lang3.math; + +import junit.framework.TestCase; + +/** + * Test cases for the {@link Fraction} class + * + * @version $Id: FractionTest.java 1153484 2011-08-03 13:39:42Z ggregory $ + */ +public class FractionTest extends TestCase { + + private static final int SKIP = 500; //53 + + public FractionTest(String name) { + super(name); + } + + //-------------------------------------------------------------------------- + + public void testConstants() { + assertEquals(0, Fraction.ZERO.getNumerator()); + assertEquals(1, Fraction.ZERO.getDenominator()); + + assertEquals(1, Fraction.ONE.getNumerator()); + assertEquals(1, Fraction.ONE.getDenominator()); + + assertEquals(1, Fraction.ONE_HALF.getNumerator()); + assertEquals(2, Fraction.ONE_HALF.getDenominator()); + + assertEquals(1, Fraction.ONE_THIRD.getNumerator()); + assertEquals(3, Fraction.ONE_THIRD.getDenominator()); + + assertEquals(2, Fraction.TWO_THIRDS.getNumerator()); + assertEquals(3, Fraction.TWO_THIRDS.getDenominator()); + + assertEquals(1, Fraction.ONE_QUARTER.getNumerator()); + assertEquals(4, Fraction.ONE_QUARTER.getDenominator()); + + assertEquals(2, Fraction.TWO_QUARTERS.getNumerator()); + assertEquals(4, Fraction.TWO_QUARTERS.getDenominator()); + + assertEquals(3, Fraction.THREE_QUARTERS.getNumerator()); + assertEquals(4, Fraction.THREE_QUARTERS.getDenominator()); + + assertEquals(1, Fraction.ONE_FIFTH.getNumerator()); + assertEquals(5, Fraction.ONE_FIFTH.getDenominator()); + + assertEquals(2, Fraction.TWO_FIFTHS.getNumerator()); + assertEquals(5, Fraction.TWO_FIFTHS.getDenominator()); + + assertEquals(3, Fraction.THREE_FIFTHS.getNumerator()); + assertEquals(5, Fraction.THREE_FIFTHS.getDenominator()); + + assertEquals(4, Fraction.FOUR_FIFTHS.getNumerator()); + assertEquals(5, Fraction.FOUR_FIFTHS.getDenominator()); + } + + public void testFactory_int_int() { + Fraction f = null; + + // zero + f = Fraction.getFraction(0, 1); + assertEquals(0, f.getNumerator()); + assertEquals(1, f.getDenominator()); + + f = Fraction.getFraction(0, 2); + assertEquals(0, f.getNumerator()); + assertEquals(2, f.getDenominator()); + + // normal + f = Fraction.getFraction(1, 1); + assertEquals(1, f.getNumerator()); + assertEquals(1, f.getDenominator()); + + f = Fraction.getFraction(2, 1); + assertEquals(2, f.getNumerator()); + assertEquals(1, f.getDenominator()); + + f = Fraction.getFraction(23, 345); + assertEquals(23, f.getNumerator()); + assertEquals(345, f.getDenominator()); + + // improper + f = Fraction.getFraction(22, 7); + assertEquals(22, f.getNumerator()); + assertEquals(7, f.getDenominator()); + + // negatives + f = Fraction.getFraction(-6, 10); + assertEquals(-6, f.getNumerator()); + assertEquals(10, f.getDenominator()); + + f = Fraction.getFraction(6, -10); + assertEquals(-6, f.getNumerator()); + assertEquals(10, f.getDenominator()); + + f = Fraction.getFraction(-6, -10); + assertEquals(6, f.getNumerator()); + assertEquals(10, f.getDenominator()); + + // zero denominator + try { + f = Fraction.getFraction(1, 0); + fail("expecting ArithmeticException"); + } catch (ArithmeticException ex) {} + + try { + f = Fraction.getFraction(2, 0); + fail("expecting ArithmeticException"); + } catch (ArithmeticException ex) {} + + try { + f = Fraction.getFraction(-3, 0); + fail("expecting ArithmeticException"); + } catch (ArithmeticException ex) {} + + // very large: can't represent as unsimplified fraction, although + try { + f = Fraction.getFraction(4, Integer.MIN_VALUE); + fail("expecting ArithmeticException"); + } catch (ArithmeticException ex) {} + try { + f = Fraction.getFraction(1, Integer.MIN_VALUE); + fail("expecting ArithmeticException"); + } catch (ArithmeticException ex) {} + } + + public void testFactory_int_int_int() { + Fraction f = null; + + // zero + f = Fraction.getFraction(0, 0, 2); + assertEquals(0, f.getNumerator()); + assertEquals(2, f.getDenominator()); + + f = Fraction.getFraction(2, 0, 2); + assertEquals(4, f.getNumerator()); + assertEquals(2, f.getDenominator()); + + f = Fraction.getFraction(0, 1, 2); + assertEquals(1, f.getNumerator()); + assertEquals(2, f.getDenominator()); + + // normal + f = Fraction.getFraction(1, 1, 2); + assertEquals(3, f.getNumerator()); + assertEquals(2, f.getDenominator()); + + // negatives + try { + f = Fraction.getFraction(1, -6, -10); + fail("expecting ArithmeticException"); + } catch (ArithmeticException ex) {} + + try { + f = Fraction.getFraction(1, -6, -10); + fail("expecting ArithmeticException"); + } catch (ArithmeticException ex) {} + + try { + f = Fraction.getFraction(1, -6, -10); + fail("expecting ArithmeticException"); + } catch (ArithmeticException ex) {} + + // negative whole + f = Fraction.getFraction(-1, 6, 10); + assertEquals(-16, f.getNumerator()); + assertEquals(10, f.getDenominator()); + + try { + f = Fraction.getFraction(-1, -6, 10); + fail("expecting ArithmeticException"); + } catch (ArithmeticException ex) {} + + try { + f = Fraction.getFraction(-1, 6, -10); + fail("expecting ArithmeticException"); + } catch (ArithmeticException ex) {} + + try { + f = Fraction.getFraction(-1, -6, -10); + fail("expecting ArithmeticException"); + } catch (ArithmeticException ex) {} + + // zero denominator + try { + f = Fraction.getFraction(0, 1, 0); + fail("expecting ArithmeticException"); + } catch (ArithmeticException ex) {} + + try { + f = Fraction.getFraction(1, 2, 0); + fail("expecting ArithmeticException"); + } catch (ArithmeticException ex) {} + + try { + f = Fraction.getFraction(-1, -3, 0); + fail("expecting ArithmeticException"); + } catch (ArithmeticException ex) {} + + try { + f = Fraction.getFraction(Integer.MAX_VALUE, 1, 2); + fail("expecting ArithmeticException"); + } catch (ArithmeticException ex) {} + + try { + f = Fraction.getFraction(-Integer.MAX_VALUE, 1, 2); + fail("expecting ArithmeticException"); + } catch (ArithmeticException ex) {} + + // very large + f = Fraction.getFraction(-1, 0, Integer.MAX_VALUE); + assertEquals(-Integer.MAX_VALUE, f.getNumerator()); + assertEquals(Integer.MAX_VALUE, f.getDenominator()); + + try { + // negative denominators not allowed in this constructor. + f = Fraction.getFraction(0, 4, Integer.MIN_VALUE); + fail("expecting ArithmeticException"); + } catch (ArithmeticException ex) {} + try { + f = Fraction.getFraction(1, 1, Integer.MAX_VALUE); + fail("expecting ArithmeticException"); + } catch (ArithmeticException ex) {} + try { + f = Fraction.getFraction(-1, 2, Integer.MAX_VALUE); + fail("expecting ArithmeticException"); + } catch (ArithmeticException ex) {} + } + public void testReducedFactory_int_int() { + Fraction f = null; + + // zero + f = Fraction.getReducedFraction(0, 1); + assertEquals(0, f.getNumerator()); + assertEquals(1, f.getDenominator()); + + // normal + f = Fraction.getReducedFraction(1, 1); + assertEquals(1, f.getNumerator()); + assertEquals(1, f.getDenominator()); + + f = Fraction.getReducedFraction(2, 1); + assertEquals(2, f.getNumerator()); + assertEquals(1, f.getDenominator()); + + // improper + f = Fraction.getReducedFraction(22, 7); + assertEquals(22, f.getNumerator()); + assertEquals(7, f.getDenominator()); + + // negatives + f = Fraction.getReducedFraction(-6, 10); + assertEquals(-3, f.getNumerator()); + assertEquals(5, f.getDenominator()); + + f = Fraction.getReducedFraction(6, -10); + assertEquals(-3, f.getNumerator()); + assertEquals(5, f.getDenominator()); + + f = Fraction.getReducedFraction(-6, -10); + assertEquals(3, f.getNumerator()); + assertEquals(5, f.getDenominator()); + + // zero denominator + try { + f = Fraction.getReducedFraction(1, 0); + fail("expecting ArithmeticException"); + } catch (ArithmeticException ex) {} + + try { + f = Fraction.getReducedFraction(2, 0); + fail("expecting ArithmeticException"); + } catch (ArithmeticException ex) {} + + try { + f = Fraction.getReducedFraction(-3, 0); + fail("expecting ArithmeticException"); + } catch (ArithmeticException ex) {} + + // reduced + f = Fraction.getReducedFraction(0, 2); + assertEquals(0, f.getNumerator()); + assertEquals(1, f.getDenominator()); + + f = Fraction.getReducedFraction(2, 2); + assertEquals(1, f.getNumerator()); + assertEquals(1, f.getDenominator()); + + f = Fraction.getReducedFraction(2, 4); + assertEquals(1, f.getNumerator()); + assertEquals(2, f.getDenominator()); + + f = Fraction.getReducedFraction(15, 10); + assertEquals(3, f.getNumerator()); + assertEquals(2, f.getDenominator()); + + f = Fraction.getReducedFraction(121, 22); + assertEquals(11, f.getNumerator()); + assertEquals(2, f.getDenominator()); + + // Extreme values + // OK, can reduce before negating + f = Fraction.getReducedFraction(-2, Integer.MIN_VALUE); + assertEquals(1, f.getNumerator()); + assertEquals(-(Integer.MIN_VALUE / 2), f.getDenominator()); + + // Can't reduce, negation will throw + try { + f = Fraction.getReducedFraction(-7, Integer.MIN_VALUE); + fail("Expecting ArithmeticException"); + } catch (ArithmeticException ex) {} + + // LANG-662 + f = Fraction.getReducedFraction(Integer.MIN_VALUE, 2); + assertEquals(Integer.MIN_VALUE / 2, f.getNumerator()); + assertEquals(1, f.getDenominator()); + } + + public void testFactory_double() { + Fraction f = null; + + try { + f = Fraction.getFraction(Double.NaN); + fail("expecting ArithmeticException"); + } catch (ArithmeticException ex) {} + + try { + f = Fraction.getFraction(Double.POSITIVE_INFINITY); + fail("expecting ArithmeticException"); + } catch (ArithmeticException ex) {} + + try { + f = Fraction.getFraction(Double.NEGATIVE_INFINITY); + fail("expecting ArithmeticException"); + } catch (ArithmeticException ex) {} + + try { + f = Fraction.getFraction((double) Integer.MAX_VALUE + 1); + fail("expecting ArithmeticException"); + } catch (ArithmeticException ex) {} + + // zero + f = Fraction.getFraction(0.0d); + assertEquals(0, f.getNumerator()); + assertEquals(1, f.getDenominator()); + + // one + f = Fraction.getFraction(1.0d); + assertEquals(1, f.getNumerator()); + assertEquals(1, f.getDenominator()); + + // one half + f = Fraction.getFraction(0.5d); + assertEquals(1, f.getNumerator()); + assertEquals(2, f.getDenominator()); + + // negative + f = Fraction.getFraction(-0.875d); + assertEquals(-7, f.getNumerator()); + assertEquals(8, f.getDenominator()); + + // over 1 + f = Fraction.getFraction(1.25d); + assertEquals(5, f.getNumerator()); + assertEquals(4, f.getDenominator()); + + // two thirds + f = Fraction.getFraction(0.66666d); + assertEquals(2, f.getNumerator()); + assertEquals(3, f.getDenominator()); + + // small + f = Fraction.getFraction(1.0d/10001d); + assertEquals(0, f.getNumerator()); + assertEquals(1, f.getDenominator()); + + // normal + Fraction f2 = null; + for (int i = 1; i <= 100; i++) { // denominator + for (int j = 1; j <= i; j++) { // numerator + try { + f = Fraction.getFraction((double) j / (double) i); + } catch (ArithmeticException ex) { + System.err.println(j + " " + i); + throw ex; + } + f2 = Fraction.getReducedFraction(j, i); + assertEquals(f2.getNumerator(), f.getNumerator()); + assertEquals(f2.getDenominator(), f.getDenominator()); + } + } + // save time by skipping some tests! ( + for (int i = 1001; i <= 10000; i+=SKIP) { // denominator + for (int j = 1; j <= i; j++) { // numerator + try { + f = Fraction.getFraction((double) j / (double) i); + } catch (ArithmeticException ex) { + System.err.println(j + " " + i); + throw ex; + } + f2 = Fraction.getReducedFraction(j, i); + assertEquals(f2.getNumerator(), f.getNumerator()); + assertEquals(f2.getDenominator(), f.getDenominator()); + } + } + } + + public void testFactory_String() { + try { + Fraction.getFraction(null); + fail("expecting IllegalArgumentException"); + } catch (IllegalArgumentException ex) {} + } + + + public void testFactory_String_double() { + Fraction f = null; + + f = Fraction.getFraction("0.0"); + assertEquals(0, f.getNumerator()); + assertEquals(1, f.getDenominator()); + + f = Fraction.getFraction("0.2"); + assertEquals(1, f.getNumerator()); + assertEquals(5, f.getDenominator()); + + f = Fraction.getFraction("0.5"); + assertEquals(1, f.getNumerator()); + assertEquals(2, f.getDenominator()); + + f = Fraction.getFraction("0.66666"); + assertEquals(2, f.getNumerator()); + assertEquals(3, f.getDenominator()); + + try { + f = Fraction.getFraction("2.3R"); + fail("Expecting NumberFormatException"); + } catch (NumberFormatException ex) {} + + try { + f = Fraction.getFraction("2147483648"); // too big + fail("Expecting NumberFormatException"); + } catch (NumberFormatException ex) {} + + try { + f = Fraction.getFraction("."); + fail("Expecting NumberFormatException"); + } catch (NumberFormatException ex) {} + } + + public void testFactory_String_proper() { + Fraction f = null; + + f = Fraction.getFraction("0 0/1"); + assertEquals(0, f.getNumerator()); + assertEquals(1, f.getDenominator()); + + f = Fraction.getFraction("1 1/5"); + assertEquals(6, f.getNumerator()); + assertEquals(5, f.getDenominator()); + + f = Fraction.getFraction("7 1/2"); + assertEquals(15, f.getNumerator()); + assertEquals(2, f.getDenominator()); + + f = Fraction.getFraction("1 2/4"); + assertEquals(6, f.getNumerator()); + assertEquals(4, f.getDenominator()); + + f = Fraction.getFraction("-7 1/2"); + assertEquals(-15, f.getNumerator()); + assertEquals(2, f.getDenominator()); + + f = Fraction.getFraction("-1 2/4"); + assertEquals(-6, f.getNumerator()); + assertEquals(4, f.getDenominator()); + + try { + f = Fraction.getFraction("2 3"); + fail("expecting NumberFormatException"); + } catch (NumberFormatException ex) {} + + try { + f = Fraction.getFraction("a 3"); + fail("expecting NumberFormatException"); + } catch (NumberFormatException ex) {} + + try { + f = Fraction.getFraction("2 b/4"); + fail("expecting NumberFormatException"); + } catch (NumberFormatException ex) {} + + try { + f = Fraction.getFraction("2 "); + fail("expecting NumberFormatException"); + } catch (NumberFormatException ex) {} + + try { + f = Fraction.getFraction(" 3"); + fail("expecting NumberFormatException"); + } catch (NumberFormatException ex) {} + + try { + f = Fraction.getFraction(" "); + fail("expecting NumberFormatException"); + } catch (NumberFormatException ex) {} + } + + public void testFactory_String_improper() { + Fraction f = null; + + f = Fraction.getFraction("0/1"); + assertEquals(0, f.getNumerator()); + assertEquals(1, f.getDenominator()); + + f = Fraction.getFraction("1/5"); + assertEquals(1, f.getNumerator()); + assertEquals(5, f.getDenominator()); + + f = Fraction.getFraction("1/2"); + assertEquals(1, f.getNumerator()); + assertEquals(2, f.getDenominator()); + + f = Fraction.getFraction("2/3"); + assertEquals(2, f.getNumerator()); + assertEquals(3, f.getDenominator()); + + f = Fraction.getFraction("7/3"); + assertEquals(7, f.getNumerator()); + assertEquals(3, f.getDenominator()); + + f = Fraction.getFraction("2/4"); + assertEquals(2, f.getNumerator()); + assertEquals(4, f.getDenominator()); + + try { + f = Fraction.getFraction("2/d"); + fail("expecting NumberFormatException"); + } catch (NumberFormatException ex) {} + + try { + f = Fraction.getFraction("2e/3"); + fail("expecting NumberFormatException"); + } catch (NumberFormatException ex) {} + + try { + f = Fraction.getFraction("2/"); + fail("expecting NumberFormatException"); + } catch (NumberFormatException ex) {} + + try { + f = Fraction.getFraction("/"); + fail("expecting NumberFormatException"); + } catch (NumberFormatException ex) {} + } + + public void testGets() { + Fraction f = null; + + f = Fraction.getFraction(3, 5, 6); + assertEquals(23, f.getNumerator()); + assertEquals(3, f.getProperWhole()); + assertEquals(5, f.getProperNumerator()); + assertEquals(6, f.getDenominator()); + + f = Fraction.getFraction(-3, 5, 6); + assertEquals(-23, f.getNumerator()); + assertEquals(-3, f.getProperWhole()); + assertEquals(5, f.getProperNumerator()); + assertEquals(6, f.getDenominator()); + + f = Fraction.getFraction(Integer.MIN_VALUE, 0, 1); + assertEquals(Integer.MIN_VALUE, f.getNumerator()); + assertEquals(Integer.MIN_VALUE, f.getProperWhole()); + assertEquals(0, f.getProperNumerator()); + assertEquals(1, f.getDenominator()); + } + + public void testConversions() { + Fraction f = null; + + f = Fraction.getFraction(3, 7, 8); + assertEquals(3, f.intValue()); + assertEquals(3L, f.longValue()); + assertEquals(3.875f, f.floatValue(), 0.00001f); + assertEquals(3.875d, f.doubleValue(), 0.00001d); + } + + public void testReduce() { + Fraction f = null; + + f = Fraction.getFraction(50, 75); + Fraction result = f.reduce(); + assertEquals(2, result.getNumerator()); + assertEquals(3, result.getDenominator()); + + f = Fraction.getFraction(-2, -3); + result = f.reduce(); + assertEquals(2, result.getNumerator()); + assertEquals(3, result.getDenominator()); + + f = Fraction.getFraction(2, -3); + result = f.reduce(); + assertEquals(-2, result.getNumerator()); + assertEquals(3, result.getDenominator()); + + f = Fraction.getFraction(-2, 3); + result = f.reduce(); + assertEquals(-2, result.getNumerator()); + assertEquals(3, result.getDenominator()); + assertSame(f, result); + + f = Fraction.getFraction(2, 3); + result = f.reduce(); + assertEquals(2, result.getNumerator()); + assertEquals(3, result.getDenominator()); + assertSame(f, result); + + f = Fraction.getFraction(0, 1); + result = f.reduce(); + assertEquals(0, result.getNumerator()); + assertEquals(1, result.getDenominator()); + assertSame(f, result); + + f = Fraction.getFraction(0, 100); + result = f.reduce(); + assertEquals(0, result.getNumerator()); + assertEquals(1, result.getDenominator()); + assertSame(result, Fraction.ZERO); + + f = Fraction.getFraction(Integer.MIN_VALUE, 2); + result = f.reduce(); + assertEquals(Integer.MIN_VALUE / 2, result.getNumerator()); + assertEquals(1, result.getDenominator()); + } + + public void testInvert() { + Fraction f = null; + + f = Fraction.getFraction(50, 75); + f = f.invert(); + assertEquals(75, f.getNumerator()); + assertEquals(50, f.getDenominator()); + + f = Fraction.getFraction(4, 3); + f = f.invert(); + assertEquals(3, f.getNumerator()); + assertEquals(4, f.getDenominator()); + + f = Fraction.getFraction(-15, 47); + f = f.invert(); + assertEquals(-47, f.getNumerator()); + assertEquals(15, f.getDenominator()); + + f = Fraction.getFraction(0, 3); + try { + f = f.invert(); + fail("expecting ArithmeticException"); + } catch (ArithmeticException ex) {} + + // large values + f = Fraction.getFraction(Integer.MIN_VALUE, 1); + try { + f = f.invert(); + fail("expecting ArithmeticException"); + } catch (ArithmeticException ex) {} + + f = Fraction.getFraction(Integer.MAX_VALUE, 1); + f = f.invert(); + assertEquals(1, f.getNumerator()); + assertEquals(Integer.MAX_VALUE, f.getDenominator()); + } + + public void testNegate() { + Fraction f = null; + + f = Fraction.getFraction(50, 75); + f = f.negate(); + assertEquals(-50, f.getNumerator()); + assertEquals(75, f.getDenominator()); + + f = Fraction.getFraction(-50, 75); + f = f.negate(); + assertEquals(50, f.getNumerator()); + assertEquals(75, f.getDenominator()); + + // large values + f = Fraction.getFraction(Integer.MAX_VALUE-1, Integer.MAX_VALUE); + f = f.negate(); + assertEquals(Integer.MIN_VALUE+2, f.getNumerator()); + assertEquals(Integer.MAX_VALUE, f.getDenominator()); + + f = Fraction.getFraction(Integer.MIN_VALUE, 1); + try { + f = f.negate(); + fail("expecting ArithmeticException"); + } catch (ArithmeticException ex) {} + } + + public void testAbs() { + Fraction f = null; + + f = Fraction.getFraction(50, 75); + f = f.abs(); + assertEquals(50, f.getNumerator()); + assertEquals(75, f.getDenominator()); + + f = Fraction.getFraction(-50, 75); + f = f.abs(); + assertEquals(50, f.getNumerator()); + assertEquals(75, f.getDenominator()); + + f = Fraction.getFraction(Integer.MAX_VALUE, 1); + f = f.abs(); + assertEquals(Integer.MAX_VALUE, f.getNumerator()); + assertEquals(1, f.getDenominator()); + + f = Fraction.getFraction(Integer.MAX_VALUE, -1); + f = f.abs(); + assertEquals(Integer.MAX_VALUE, f.getNumerator()); + assertEquals(1, f.getDenominator()); + + f = Fraction.getFraction(Integer.MIN_VALUE, 1); + try { + f = f.abs(); + fail("expecting ArithmeticException"); + } catch (ArithmeticException ex) {} + } + + public void testPow() { + Fraction f = null; + + f = Fraction.getFraction(3, 5); + assertEquals(Fraction.ONE, f.pow(0)); + + f = Fraction.getFraction(3, 5); + assertSame(f, f.pow(1)); + assertEquals(f, f.pow(1)); + + f = Fraction.getFraction(3, 5); + f = f.pow(2); + assertEquals(9, f.getNumerator()); + assertEquals(25, f.getDenominator()); + + f = Fraction.getFraction(3, 5); + f = f.pow(3); + assertEquals(27, f.getNumerator()); + assertEquals(125, f.getDenominator()); + + f = Fraction.getFraction(3, 5); + f = f.pow(-1); + assertEquals(5, f.getNumerator()); + assertEquals(3, f.getDenominator()); + + f = Fraction.getFraction(3, 5); + f = f.pow(-2); + assertEquals(25, f.getNumerator()); + assertEquals(9, f.getDenominator()); + + // check unreduced fractions stay that way. + f = Fraction.getFraction(6, 10); + assertEquals(Fraction.ONE, f.pow(0)); + + f = Fraction.getFraction(6, 10); + assertEquals(f, f.pow(1)); + assertFalse(f.pow(1).equals(Fraction.getFraction(3,5))); + + f = Fraction.getFraction(6, 10); + f = f.pow(2); + assertEquals(9, f.getNumerator()); + assertEquals(25, f.getDenominator()); + + f = Fraction.getFraction(6, 10); + f = f.pow(3); + assertEquals(27, f.getNumerator()); + assertEquals(125, f.getDenominator()); + + f = Fraction.getFraction(6, 10); + f = f.pow(-1); + assertEquals(10, f.getNumerator()); + assertEquals(6, f.getDenominator()); + + f = Fraction.getFraction(6, 10); + f = f.pow(-2); + assertEquals(25, f.getNumerator()); + assertEquals(9, f.getDenominator()); + + // zero to any positive power is still zero. + f = Fraction.getFraction(0, 1231); + f = f.pow(1); + assertTrue(0==f.compareTo(Fraction.ZERO)); + assertEquals(0, f.getNumerator()); + assertEquals(1231, f.getDenominator()); + f = f.pow(2); + assertTrue(0==f.compareTo(Fraction.ZERO)); + assertEquals(0, f.getNumerator()); + assertEquals(1, f.getDenominator()); + + // zero to negative powers should throw an exception + try { + f = f.pow(-1); + fail("expecting ArithmeticException"); + } catch (ArithmeticException ex) {} + try { + f = f.pow(Integer.MIN_VALUE); + fail("expecting ArithmeticException"); + } catch (ArithmeticException ex) {} + + // one to any power is still one. + f = Fraction.getFraction(1, 1); + f = f.pow(0); + assertEquals(f, Fraction.ONE); + f = f.pow(1); + assertEquals(f, Fraction.ONE); + f = f.pow(-1); + assertEquals(f, Fraction.ONE); + f = f.pow(Integer.MAX_VALUE); + assertEquals(f, Fraction.ONE); + f = f.pow(Integer.MIN_VALUE); + assertEquals(f, Fraction.ONE); + + f = Fraction.getFraction(Integer.MAX_VALUE, 1); + try { + f = f.pow(2); + fail("expecting ArithmeticException"); + } catch (ArithmeticException ex) {} + + // Numerator growing too negative during the pow operation. + f = Fraction.getFraction(Integer.MIN_VALUE, 1); + try { + f = f.pow(3); + fail("expecting ArithmeticException"); + } catch (ArithmeticException ex) {} + + f = Fraction.getFraction(65536, 1); + try { + f = f.pow(2); + fail("expecting ArithmeticException"); + } catch (ArithmeticException ex) {} + } + + public void testAdd() { + Fraction f = null; + Fraction f1 = null; + Fraction f2 = null; + + f1 = Fraction.getFraction(3, 5); + f2 = Fraction.getFraction(1, 5); + f = f1.add(f2); + assertEquals(4, f.getNumerator()); + assertEquals(5, f.getDenominator()); + + f1 = Fraction.getFraction(3, 5); + f2 = Fraction.getFraction(2, 5); + f = f1.add(f2); + assertEquals(1, f.getNumerator()); + assertEquals(1, f.getDenominator()); + + f1 = Fraction.getFraction(3, 5); + f2 = Fraction.getFraction(3, 5); + f = f1.add(f2); + assertEquals(6, f.getNumerator()); + assertEquals(5, f.getDenominator()); + + f1 = Fraction.getFraction(3, 5); + f2 = Fraction.getFraction(-4, 5); + f = f1.add(f2); + assertEquals(-1, f.getNumerator()); + assertEquals(5, f.getDenominator()); + + f1 = Fraction.getFraction(Integer.MAX_VALUE - 1, 1); + f2 = Fraction.ONE; + f = f1.add(f2); + assertEquals(Integer.MAX_VALUE, f.getNumerator()); + assertEquals(1, f.getDenominator()); + + f1 = Fraction.getFraction(3, 5); + f2 = Fraction.getFraction(1, 2); + f = f1.add(f2); + assertEquals(11, f.getNumerator()); + assertEquals(10, f.getDenominator()); + + f1 = Fraction.getFraction(3, 8); + f2 = Fraction.getFraction(1, 6); + f = f1.add(f2); + assertEquals(13, f.getNumerator()); + assertEquals(24, f.getDenominator()); + + f1 = Fraction.getFraction(0, 5); + f2 = Fraction.getFraction(1, 5); + f = f1.add(f2); + assertSame(f2, f); + f = f2.add(f1); + assertSame(f2, f); + + f1 = Fraction.getFraction(-1, 13*13*2*2); + f2 = Fraction.getFraction(-2, 13*17*2); + f = f1.add(f2); + assertEquals(13*13*17*2*2, f.getDenominator()); + assertEquals(-17 - 2*13*2, f.getNumerator()); + + try { + f.add(null); + fail("expecting IllegalArgumentException"); + } catch (IllegalArgumentException ex) {} + + // if this fraction is added naively, it will overflow. + // check that it doesn't. + f1 = Fraction.getFraction(1,32768*3); + f2 = Fraction.getFraction(1,59049); + f = f1.add(f2); + assertEquals(52451, f.getNumerator()); + assertEquals(1934917632, f.getDenominator()); + + f1 = Fraction.getFraction(Integer.MIN_VALUE, 3); + f2 = Fraction.ONE_THIRD; + f = f1.add(f2); + assertEquals(Integer.MIN_VALUE+1, f.getNumerator()); + assertEquals(3, f.getDenominator()); + + f1 = Fraction.getFraction(Integer.MAX_VALUE - 1, 1); + f2 = Fraction.ONE; + f = f1.add(f2); + assertEquals(Integer.MAX_VALUE, f.getNumerator()); + assertEquals(1, f.getDenominator()); + + try { + f = f.add(Fraction.ONE); // should overflow + fail("expecting ArithmeticException but got: " + f.toString()); + } catch (ArithmeticException ex) {} + + // denominator should not be a multiple of 2 or 3 to trigger overflow + f1 = Fraction.getFraction(Integer.MIN_VALUE, 5); + f2 = Fraction.getFraction(-1,5); + try { + f = f1.add(f2); // should overflow + fail("expecting ArithmeticException but got: " + f.toString()); + } catch (ArithmeticException ex) {} + + try { + f= Fraction.getFraction(-Integer.MAX_VALUE, 1); + f = f.add(f); + fail("expecting ArithmeticException"); + } catch (ArithmeticException ex) {} + + try { + f= Fraction.getFraction(-Integer.MAX_VALUE, 1); + f = f.add(f); + fail("expecting ArithmeticException"); + } catch (ArithmeticException ex) {} + + f1 = Fraction.getFraction(3,327680); + f2 = Fraction.getFraction(2,59049); + try { + f = f1.add(f2); // should overflow + fail("expecting ArithmeticException but got: " + f.toString()); + } catch (ArithmeticException ex) {} + } + + public void testSubtract() { + Fraction f = null; + Fraction f1 = null; + Fraction f2 = null; + + f1 = Fraction.getFraction(3, 5); + f2 = Fraction.getFraction(1, 5); + f = f1.subtract(f2); + assertEquals(2, f.getNumerator()); + assertEquals(5, f.getDenominator()); + + f1 = Fraction.getFraction(7, 5); + f2 = Fraction.getFraction(2, 5); + f = f1.subtract(f2); + assertEquals(1, f.getNumerator()); + assertEquals(1, f.getDenominator()); + + f1 = Fraction.getFraction(3, 5); + f2 = Fraction.getFraction(3, 5); + f = f1.subtract(f2); + assertEquals(0, f.getNumerator()); + assertEquals(1, f.getDenominator()); + + f1 = Fraction.getFraction(3, 5); + f2 = Fraction.getFraction(-4, 5); + f = f1.subtract(f2); + assertEquals(7, f.getNumerator()); + assertEquals(5, f.getDenominator()); + + f1 = Fraction.getFraction(0, 5); + f2 = Fraction.getFraction(4, 5); + f = f1.subtract(f2); + assertEquals(-4, f.getNumerator()); + assertEquals(5, f.getDenominator()); + + f1 = Fraction.getFraction(0, 5); + f2 = Fraction.getFraction(-4, 5); + f = f1.subtract(f2); + assertEquals(4, f.getNumerator()); + assertEquals(5, f.getDenominator()); + + f1 = Fraction.getFraction(3, 5); + f2 = Fraction.getFraction(1, 2); + f = f1.subtract(f2); + assertEquals(1, f.getNumerator()); + assertEquals(10, f.getDenominator()); + + f1 = Fraction.getFraction(0, 5); + f2 = Fraction.getFraction(1, 5); + f = f2.subtract(f1); + assertSame(f2, f); + + try { + f.subtract(null); + fail("expecting IllegalArgumentException"); + } catch (IllegalArgumentException ex) {} + + // if this fraction is subtracted naively, it will overflow. + // check that it doesn't. + f1 = Fraction.getFraction(1,32768*3); + f2 = Fraction.getFraction(1,59049); + f = f1.subtract(f2); + assertEquals(-13085, f.getNumerator()); + assertEquals(1934917632, f.getDenominator()); + + f1 = Fraction.getFraction(Integer.MIN_VALUE, 3); + f2 = Fraction.ONE_THIRD.negate(); + f = f1.subtract(f2); + assertEquals(Integer.MIN_VALUE+1, f.getNumerator()); + assertEquals(3, f.getDenominator()); + + f1 = Fraction.getFraction(Integer.MAX_VALUE, 1); + f2 = Fraction.ONE; + f = f1.subtract(f2); + assertEquals(Integer.MAX_VALUE-1, f.getNumerator()); + assertEquals(1, f.getDenominator()); + + try { + f1 = Fraction.getFraction(1, Integer.MAX_VALUE); + f2 = Fraction.getFraction(1, Integer.MAX_VALUE - 1); + f = f1.subtract(f2); + fail("expecting ArithmeticException"); //should overflow + } catch (ArithmeticException ex) {} + + // denominator should not be a multiple of 2 or 3 to trigger overflow + f1 = Fraction.getFraction(Integer.MIN_VALUE, 5); + f2 = Fraction.getFraction(1,5); + try { + f = f1.subtract(f2); // should overflow + fail("expecting ArithmeticException but got: " + f.toString()); + } catch (ArithmeticException ex) {} + + try { + f= Fraction.getFraction(Integer.MIN_VALUE, 1); + f = f.subtract(Fraction.ONE); + fail("expecting ArithmeticException"); + } catch (ArithmeticException ex) {} + + try { + f= Fraction.getFraction(Integer.MAX_VALUE, 1); + f = f.subtract(Fraction.ONE.negate()); + fail("expecting ArithmeticException"); + } catch (ArithmeticException ex) {} + + f1 = Fraction.getFraction(3,327680); + f2 = Fraction.getFraction(2,59049); + try { + f = f1.subtract(f2); // should overflow + fail("expecting ArithmeticException but got: " + f.toString()); + } catch (ArithmeticException ex) {} + } + + public void testMultiply() { + Fraction f = null; + Fraction f1 = null; + Fraction f2 = null; + + f1 = Fraction.getFraction(3, 5); + f2 = Fraction.getFraction(2, 5); + f = f1.multiplyBy(f2); + assertEquals(6, f.getNumerator()); + assertEquals(25, f.getDenominator()); + + f1 = Fraction.getFraction(6, 10); + f2 = Fraction.getFraction(6, 10); + f = f1.multiplyBy(f2); + assertEquals(9, f.getNumerator()); + assertEquals(25, f.getDenominator()); + f = f.multiplyBy(f2); + assertEquals(27, f.getNumerator()); + assertEquals(125, f.getDenominator()); + + f1 = Fraction.getFraction(3, 5); + f2 = Fraction.getFraction(-2, 5); + f = f1.multiplyBy(f2); + assertEquals(-6, f.getNumerator()); + assertEquals(25, f.getDenominator()); + + f1 = Fraction.getFraction(-3, 5); + f2 = Fraction.getFraction(-2, 5); + f = f1.multiplyBy(f2); + assertEquals(6, f.getNumerator()); + assertEquals(25, f.getDenominator()); + + + f1 = Fraction.getFraction(0, 5); + f2 = Fraction.getFraction(2, 7); + f = f1.multiplyBy(f2); + assertSame(Fraction.ZERO, f); + + f1 = Fraction.getFraction(2, 7); + f2 = Fraction.ONE; + f = f1.multiplyBy(f2); + assertEquals(2, f.getNumerator()); + assertEquals(7, f.getDenominator()); + + f1 = Fraction.getFraction(Integer.MAX_VALUE, 1); + f2 = Fraction.getFraction(Integer.MIN_VALUE, Integer.MAX_VALUE); + f = f1.multiplyBy(f2); + assertEquals(Integer.MIN_VALUE, f.getNumerator()); + assertEquals(1, f.getDenominator()); + + try { + f.multiplyBy(null); + fail("expecting IllegalArgumentException"); + } catch (IllegalArgumentException ex) {} + + try { + f1 = Fraction.getFraction(1, Integer.MAX_VALUE); + f = f1.multiplyBy(f1); // should overflow + fail("expecting ArithmeticException"); + } catch (ArithmeticException ex) {} + + try { + f1 = Fraction.getFraction(1, -Integer.MAX_VALUE); + f = f1.multiplyBy(f1); // should overflow + fail("expecting ArithmeticException"); + } catch (ArithmeticException ex) {} + } + + public void testDivide() { + Fraction f = null; + Fraction f1 = null; + Fraction f2 = null; + + f1 = Fraction.getFraction(3, 5); + f2 = Fraction.getFraction(2, 5); + f = f1.divideBy(f2); + assertEquals(3, f.getNumerator()); + assertEquals(2, f.getDenominator()); + + f1 = Fraction.getFraction(3, 5); + f2 = Fraction.ZERO; + try { + f = f1.divideBy(f2); + fail("expecting ArithmeticException"); + } catch (ArithmeticException ex) {} + + f1 = Fraction.getFraction(0, 5); + f2 = Fraction.getFraction(2, 7); + f = f1.divideBy(f2); + assertSame(Fraction.ZERO, f); + + f1 = Fraction.getFraction(2, 7); + f2 = Fraction.ONE; + f = f1.divideBy(f2); + assertEquals(2, f.getNumerator()); + assertEquals(7, f.getDenominator()); + + f1 = Fraction.getFraction(1, Integer.MAX_VALUE); + f = f1.divideBy(f1); + assertEquals(1, f.getNumerator()); + assertEquals(1, f.getDenominator()); + + f1 = Fraction.getFraction(Integer.MIN_VALUE, Integer.MAX_VALUE); + f2 = Fraction.getFraction(1, Integer.MAX_VALUE); + f = f1.divideBy(f2); + assertEquals(Integer.MIN_VALUE, f.getNumerator()); + assertEquals(1, f.getDenominator()); + + try { + f.divideBy(null); + fail("IllegalArgumentException"); + } catch (IllegalArgumentException ex) {} + + try { + f1 = Fraction.getFraction(1, Integer.MAX_VALUE); + f = f1.divideBy(f1.invert()); // should overflow + fail("expecting ArithmeticException"); + } catch (ArithmeticException ex) {} + try { + f1 = Fraction.getFraction(1, -Integer.MAX_VALUE); + f = f1.divideBy(f1.invert()); // should overflow + fail("expecting ArithmeticException"); + } catch (ArithmeticException ex) {} + } + + public void testEquals() { + Fraction f1 = null; + Fraction f2 = null; + + f1 = Fraction.getFraction(3, 5); + assertEquals(false, f1.equals(null)); + assertEquals(false, f1.equals(new Object())); + assertEquals(false, f1.equals(Integer.valueOf(6))); + + f1 = Fraction.getFraction(3, 5); + f2 = Fraction.getFraction(2, 5); + assertEquals(false, f1.equals(f2)); + assertEquals(true, f1.equals(f1)); + assertEquals(true, f2.equals(f2)); + + f2 = Fraction.getFraction(3, 5); + assertEquals(true, f1.equals(f2)); + + f2 = Fraction.getFraction(6, 10); + assertEquals(false, f1.equals(f2)); + } + + public void testHashCode() { + Fraction f1 = Fraction.getFraction(3, 5); + Fraction f2 = Fraction.getFraction(3, 5); + + assertTrue(f1.hashCode() == f2.hashCode()); + + f2 = Fraction.getFraction(2, 5); + assertTrue(f1.hashCode() != f2.hashCode()); + + f2 = Fraction.getFraction(6, 10); + assertTrue(f1.hashCode() != f2.hashCode()); + } + + public void testCompareTo() { + Fraction f1 = null; + Fraction f2 = null; + + f1 = Fraction.getFraction(3, 5); + assertTrue(f1.compareTo(f1) == 0); + + try { + f1.compareTo(null); + fail("expecting NullPointerException"); + } catch (NullPointerException ex) {} + + f2 = Fraction.getFraction(2, 5); + assertTrue(f1.compareTo(f2) > 0); + assertTrue(f2.compareTo(f2) == 0); + + f2 = Fraction.getFraction(4, 5); + assertTrue(f1.compareTo(f2) < 0); + assertTrue(f2.compareTo(f2) == 0); + + f2 = Fraction.getFraction(3, 5); + assertTrue(f1.compareTo(f2) == 0); + assertTrue(f2.compareTo(f2) == 0); + + f2 = Fraction.getFraction(6, 10); + assertTrue(f1.compareTo(f2) == 0); + assertTrue(f2.compareTo(f2) == 0); + + f2 = Fraction.getFraction(-1, 1, Integer.MAX_VALUE); + assertTrue(f1.compareTo(f2) > 0); + assertTrue(f2.compareTo(f2) == 0); + + } + + public void testToString() { + Fraction f = null; + + f = Fraction.getFraction(3, 5); + String str = f.toString(); + assertEquals("3/5", str); + assertSame(str, f.toString()); + + f = Fraction.getFraction(7, 5); + assertEquals("7/5", f.toString()); + + f = Fraction.getFraction(4, 2); + assertEquals("4/2", f.toString()); + + f = Fraction.getFraction(0, 2); + assertEquals("0/2", f.toString()); + + f = Fraction.getFraction(2, 2); + assertEquals("2/2", f.toString()); + + f = Fraction.getFraction(Integer.MIN_VALUE, 0, 1); + assertEquals("-2147483648/1", f.toString()); + + f = Fraction.getFraction(-1, 1, Integer.MAX_VALUE); + assertEquals("-2147483648/2147483647", f.toString()); + } + + public void testToProperString() { + Fraction f = null; + + f = Fraction.getFraction(3, 5); + String str = f.toProperString(); + assertEquals("3/5", str); + assertSame(str, f.toProperString()); + + f = Fraction.getFraction(7, 5); + assertEquals("1 2/5", f.toProperString()); + + f = Fraction.getFraction(14, 10); + assertEquals("1 4/10", f.toProperString()); + + f = Fraction.getFraction(4, 2); + assertEquals("2", f.toProperString()); + + f = Fraction.getFraction(0, 2); + assertEquals("0", f.toProperString()); + + f = Fraction.getFraction(2, 2); + assertEquals("1", f.toProperString()); + + f = Fraction.getFraction(-7, 5); + assertEquals("-1 2/5", f.toProperString()); + + f = Fraction.getFraction(Integer.MIN_VALUE, 0, 1); + assertEquals("-2147483648", f.toProperString()); + + f = Fraction.getFraction(-1, 1, Integer.MAX_VALUE); + assertEquals("-1 1/2147483647", f.toProperString()); + + assertEquals("-1", Fraction.getFraction(-1).toProperString()); + } +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/math/IEEE754rUtils.java b/ApacheCommonsLang/org/apache/commons/lang3/math/IEEE754rUtils.java new file mode 100644 index 0000000..d16cdb4 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/math/IEEE754rUtils.java @@ -0,0 +1,265 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.math; + +/** + *

Provides IEEE-754r variants of NumberUtils methods.

+ * + *

See: http://en.wikipedia.org/wiki/IEEE_754r

+ * + * @since 2.4 + * @version $Id: IEEE754rUtils.java 1088899 2011-04-05 05:31:27Z bayard $ + */ +public class IEEE754rUtils { + + /** + *

Returns the minimum value in an array.

+ * + * @param array an array, must not be null or empty + * @return the minimum value in the array + * @throws IllegalArgumentException if array is null + * @throws IllegalArgumentException if array is empty + */ + public static double min(double[] array) { + // Validates input + if (array == null) { + throw new IllegalArgumentException("The Array must not be null"); + } else if (array.length == 0) { + throw new IllegalArgumentException("Array cannot be empty."); + } + + // Finds and returns min + double min = array[0]; + for (int i = 1; i < array.length; i++) { + min = min(array[i], min); + } + + return min; + } + + /** + *

Returns the minimum value in an array.

+ * + * @param array an array, must not be null or empty + * @return the minimum value in the array + * @throws IllegalArgumentException if array is null + * @throws IllegalArgumentException if array is empty + */ + public static float min(float[] array) { + // Validates input + if (array == null) { + throw new IllegalArgumentException("The Array must not be null"); + } else if (array.length == 0) { + throw new IllegalArgumentException("Array cannot be empty."); + } + + // Finds and returns min + float min = array[0]; + for (int i = 1; i < array.length; i++) { + min = min(array[i], min); + } + + return min; + } + + /** + *

Gets the minimum of three double values.

+ * + *

NaN is only returned if all numbers are NaN as per IEEE-754r.

+ * + * @param a value 1 + * @param b value 2 + * @param c value 3 + * @return the smallest of the values + */ + public static double min(double a, double b, double c) { + return min(min(a, b), c); + } + + /** + *

Gets the minimum of two double values.

+ * + *

NaN is only returned if all numbers are NaN as per IEEE-754r.

+ * + * @param a value 1 + * @param b value 2 + * @return the smallest of the values + */ + public static double min(double a, double b) { + if(Double.isNaN(a)) { + return b; + } else + if(Double.isNaN(b)) { + return a; + } else { + return Math.min(a, b); + } + } + + /** + *

Gets the minimum of three float values.

+ * + *

NaN is only returned if all numbers are NaN as per IEEE-754r.

+ * + * @param a value 1 + * @param b value 2 + * @param c value 3 + * @return the smallest of the values + */ + public static float min(float a, float b, float c) { + return min(min(a, b), c); + } + + /** + *

Gets the minimum of two float values.

+ * + *

NaN is only returned if all numbers are NaN as per IEEE-754r.

+ * + * @param a value 1 + * @param b value 2 + * @return the smallest of the values + */ + public static float min(float a, float b) { + if(Float.isNaN(a)) { + return b; + } else + if(Float.isNaN(b)) { + return a; + } else { + return Math.min(a, b); + } + } + + /** + *

Returns the maximum value in an array.

+ * + * @param array an array, must not be null or empty + * @return the minimum value in the array + * @throws IllegalArgumentException if array is null + * @throws IllegalArgumentException if array is empty + */ + public static double max(double[] array) { + // Validates input + if (array== null) { + throw new IllegalArgumentException("The Array must not be null"); + } else if (array.length == 0) { + throw new IllegalArgumentException("Array cannot be empty."); + } + + // Finds and returns max + double max = array[0]; + for (int j = 1; j < array.length; j++) { + max = max(array[j], max); + } + + return max; + } + + /** + *

Returns the maximum value in an array.

+ * + * @param array an array, must not be null or empty + * @return the minimum value in the array + * @throws IllegalArgumentException if array is null + * @throws IllegalArgumentException if array is empty + */ + public static float max(float[] array) { + // Validates input + if (array == null) { + throw new IllegalArgumentException("The Array must not be null"); + } else if (array.length == 0) { + throw new IllegalArgumentException("Array cannot be empty."); + } + + // Finds and returns max + float max = array[0]; + for (int j = 1; j < array.length; j++) { + max = max(array[j], max); + } + + return max; + } + + /** + *

Gets the maximum of three double values.

+ * + *

NaN is only returned if all numbers are NaN as per IEEE-754r.

+ * + * @param a value 1 + * @param b value 2 + * @param c value 3 + * @return the largest of the values + */ + public static double max(double a, double b, double c) { + return max(max(a, b), c); + } + + /** + *

Gets the maximum of two double values.

+ * + *

NaN is only returned if all numbers are NaN as per IEEE-754r.

+ * + * @param a value 1 + * @param b value 2 + * @return the largest of the values + */ + public static double max(double a, double b) { + if(Double.isNaN(a)) { + return b; + } else + if(Double.isNaN(b)) { + return a; + } else { + return Math.max(a, b); + } + } + + /** + *

Gets the maximum of three float values.

+ * + *

NaN is only returned if all numbers are NaN as per IEEE-754r.

+ * + * @param a value 1 + * @param b value 2 + * @param c value 3 + * @return the largest of the values + */ + public static float max(float a, float b, float c) { + return max(max(a, b), c); + } + + /** + *

Gets the maximum of two float values.

+ * + *

NaN is only returned if all numbers are NaN as per IEEE-754r.

+ * + * @param a value 1 + * @param b value 2 + * @return the largest of the values + */ + public static float max(float a, float b) { + if(Float.isNaN(a)) { + return b; + } else + if(Float.isNaN(b)) { + return a; + } else { + return Math.max(a, b); + } + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/math/IEEE754rUtilsTest.java b/ApacheCommonsLang/org/apache/commons/lang3/math/IEEE754rUtilsTest.java new file mode 100644 index 0000000..3abc3e4 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/math/IEEE754rUtilsTest.java @@ -0,0 +1,100 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.math; + +import junit.framework.TestCase; + +/** + * Unit tests {@link org.apache.commons.lang3.math.IEEE754rUtils}. + * + * @version $Id: IEEE754rUtilsTest.java 889219 2009-12-10 12:02:50Z bayard $ + */ +public class IEEE754rUtilsTest extends TestCase { + + public void testLang381() { + assertEquals(1.2, IEEE754rUtils.min(1.2, 2.5, Double.NaN), 0.01); + assertEquals(2.5, IEEE754rUtils.max(1.2, 2.5, Double.NaN), 0.01); + assertTrue(Double.isNaN(IEEE754rUtils.max(Double.NaN, Double.NaN, Double.NaN))); + assertEquals(1.2f, IEEE754rUtils.min(1.2f, 2.5f, Float.NaN), 0.01); + assertEquals(2.5f, IEEE754rUtils.max(1.2f, 2.5f, Float.NaN), 0.01); + assertTrue(Float.isNaN(IEEE754rUtils.max(Float.NaN, Float.NaN, Float.NaN))); + + double[] a = new double[] { 1.2, Double.NaN, 3.7, 27.0, 42.0, Double.NaN }; + assertEquals(42.0, IEEE754rUtils.max(a), 0.01); + assertEquals(1.2, IEEE754rUtils.min(a), 0.01); + + double[] b = new double[] { Double.NaN, 1.2, Double.NaN, 3.7, 27.0, 42.0, Double.NaN }; + assertEquals(42.0, IEEE754rUtils.max(b), 0.01); + assertEquals(1.2, IEEE754rUtils.min(b), 0.01); + + float[] aF = new float[] { 1.2f, Float.NaN, 3.7f, 27.0f, 42.0f, Float.NaN }; + assertEquals(1.2f, IEEE754rUtils.min(aF), 0.01); + assertEquals(42.0f, IEEE754rUtils.max(aF), 0.01); + + float[] bF = new float[] { Float.NaN, 1.2f, Float.NaN, 3.7f, 27.0f, 42.0f, Float.NaN }; + assertEquals(1.2f, IEEE754rUtils.min(bF), 0.01); + assertEquals(42.0f, IEEE754rUtils.max(bF), 0.01); + } + + public void testEnforceExceptions() { + try { + IEEE754rUtils.min( (float[]) null); + fail("IllegalArgumentException expected for null input"); + } catch(IllegalArgumentException iae) { /* expected */ } + + try { + IEEE754rUtils.min(new float[0]); + fail("IllegalArgumentException expected for empty input"); + } catch(IllegalArgumentException iae) { /* expected */ } + + try { + IEEE754rUtils.max( (float[]) null); + fail("IllegalArgumentException expected for null input"); + } catch(IllegalArgumentException iae) { /* expected */ } + + try { + IEEE754rUtils.max(new float[0]); + fail("IllegalArgumentException expected for empty input"); + } catch(IllegalArgumentException iae) { /* expected */ } + + try { + IEEE754rUtils.min( (double[]) null); + fail("IllegalArgumentException expected for null input"); + } catch(IllegalArgumentException iae) { /* expected */ } + + try { + IEEE754rUtils.min(new double[0]); + fail("IllegalArgumentException expected for empty input"); + } catch(IllegalArgumentException iae) { /* expected */ } + + try { + IEEE754rUtils.max( (double[]) null); + fail("IllegalArgumentException expected for null input"); + } catch(IllegalArgumentException iae) { /* expected */ } + + try { + IEEE754rUtils.max(new double[0]); + fail("IllegalArgumentException expected for empty input"); + } catch(IllegalArgumentException iae) { /* expected */ } + + } + + public void testConstructorExists() { + new IEEE754rUtils(); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/math/NumberUtils.java b/ApacheCommonsLang/org/apache/commons/lang3/math/NumberUtils.java new file mode 100644 index 0000000..4395836 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/math/NumberUtils.java @@ -0,0 +1,1414 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.math; + +import java.math.BigDecimal; +import java.math.BigInteger; + +import org.apache.commons.lang3.StringUtils; + +/** + *

Provides extra functionality for Java Number classes.

+ * + * @since 2.0 + * @version $Id: NumberUtils.java 1199816 2011-11-09 16:11:34Z bayard $ + */ +public class NumberUtils { + + /** Reusable Long constant for zero. */ + public static final Long LONG_ZERO = Long.valueOf(0L); + /** Reusable Long constant for one. */ + public static final Long LONG_ONE = Long.valueOf(1L); + /** Reusable Long constant for minus one. */ + public static final Long LONG_MINUS_ONE = Long.valueOf(-1L); + /** Reusable Integer constant for zero. */ + public static final Integer INTEGER_ZERO = Integer.valueOf(0); + /** Reusable Integer constant for one. */ + public static final Integer INTEGER_ONE = Integer.valueOf(1); + /** Reusable Integer constant for minus one. */ + public static final Integer INTEGER_MINUS_ONE = Integer.valueOf(-1); + /** Reusable Short constant for zero. */ + public static final Short SHORT_ZERO = Short.valueOf((short) 0); + /** Reusable Short constant for one. */ + public static final Short SHORT_ONE = Short.valueOf((short) 1); + /** Reusable Short constant for minus one. */ + public static final Short SHORT_MINUS_ONE = Short.valueOf((short) -1); + /** Reusable Byte constant for zero. */ + public static final Byte BYTE_ZERO = Byte.valueOf((byte) 0); + /** Reusable Byte constant for one. */ + public static final Byte BYTE_ONE = Byte.valueOf((byte) 1); + /** Reusable Byte constant for minus one. */ + public static final Byte BYTE_MINUS_ONE = Byte.valueOf((byte) -1); + /** Reusable Double constant for zero. */ + public static final Double DOUBLE_ZERO = Double.valueOf(0.0d); + /** Reusable Double constant for one. */ + public static final Double DOUBLE_ONE = Double.valueOf(1.0d); + /** Reusable Double constant for minus one. */ + public static final Double DOUBLE_MINUS_ONE = Double.valueOf(-1.0d); + /** Reusable Float constant for zero. */ + public static final Float FLOAT_ZERO = Float.valueOf(0.0f); + /** Reusable Float constant for one. */ + public static final Float FLOAT_ONE = Float.valueOf(1.0f); + /** Reusable Float constant for minus one. */ + public static final Float FLOAT_MINUS_ONE = Float.valueOf(-1.0f); + + /** + *

NumberUtils instances should NOT be constructed in standard programming. + * Instead, the class should be used as NumberUtils.toInt("6");.

+ * + *

This constructor is public to permit tools that require a JavaBean instance + * to operate.

+ */ + public NumberUtils() { + super(); + } + + //----------------------------------------------------------------------- + /** + *

Convert a String to an int, returning + * zero if the conversion fails.

+ * + *

If the string is null, zero is returned.

+ * + *
+     *   NumberUtils.toInt(null) = 0
+     *   NumberUtils.toInt("")   = 0
+     *   NumberUtils.toInt("1")  = 1
+     * 
+ * + * @param str the string to convert, may be null + * @return the int represented by the string, or zero if + * conversion fails + * @since 2.1 + */ + public static int toInt(String str) { + return toInt(str, 0); + } + + /** + *

Convert a String to an int, returning a + * default value if the conversion fails.

+ * + *

If the string is null, the default value is returned.

+ * + *
+     *   NumberUtils.toInt(null, 1) = 1
+     *   NumberUtils.toInt("", 1)   = 1
+     *   NumberUtils.toInt("1", 0)  = 1
+     * 
+ * + * @param str the string to convert, may be null + * @param defaultValue the default value + * @return the int represented by the string, or the default if conversion fails + * @since 2.1 + */ + public static int toInt(String str, int defaultValue) { + if(str == null) { + return defaultValue; + } + try { + return Integer.parseInt(str); + } catch (NumberFormatException nfe) { + return defaultValue; + } + } + + /** + *

Convert a String to a long, returning + * zero if the conversion fails.

+ * + *

If the string is null, zero is returned.

+ * + *
+     *   NumberUtils.toLong(null) = 0L
+     *   NumberUtils.toLong("")   = 0L
+     *   NumberUtils.toLong("1")  = 1L
+     * 
+ * + * @param str the string to convert, may be null + * @return the long represented by the string, or 0 if + * conversion fails + * @since 2.1 + */ + public static long toLong(String str) { + return toLong(str, 0L); + } + + /** + *

Convert a String to a long, returning a + * default value if the conversion fails.

+ * + *

If the string is null, the default value is returned.

+ * + *
+     *   NumberUtils.toLong(null, 1L) = 1L
+     *   NumberUtils.toLong("", 1L)   = 1L
+     *   NumberUtils.toLong("1", 0L)  = 1L
+     * 
+ * + * @param str the string to convert, may be null + * @param defaultValue the default value + * @return the long represented by the string, or the default if conversion fails + * @since 2.1 + */ + public static long toLong(String str, long defaultValue) { + if (str == null) { + return defaultValue; + } + try { + return Long.parseLong(str); + } catch (NumberFormatException nfe) { + return defaultValue; + } + } + + /** + *

Convert a String to a float, returning + * 0.0f if the conversion fails.

+ * + *

If the string str is null, + * 0.0f is returned.

+ * + *
+     *   NumberUtils.toFloat(null)   = 0.0f
+     *   NumberUtils.toFloat("")     = 0.0f
+     *   NumberUtils.toFloat("1.5")  = 1.5f
+     * 
+ * + * @param str the string to convert, may be null + * @return the float represented by the string, or 0.0f + * if conversion fails + * @since 2.1 + */ + public static float toFloat(String str) { + return toFloat(str, 0.0f); + } + + /** + *

Convert a String to a float, returning a + * default value if the conversion fails.

+ * + *

If the string str is null, the default + * value is returned.

+ * + *
+     *   NumberUtils.toFloat(null, 1.1f)   = 1.0f
+     *   NumberUtils.toFloat("", 1.1f)     = 1.1f
+     *   NumberUtils.toFloat("1.5", 0.0f)  = 1.5f
+     * 
+ * + * @param str the string to convert, may be null + * @param defaultValue the default value + * @return the float represented by the string, or defaultValue + * if conversion fails + * @since 2.1 + */ + public static float toFloat(String str, float defaultValue) { + if (str == null) { + return defaultValue; + } + try { + return Float.parseFloat(str); + } catch (NumberFormatException nfe) { + return defaultValue; + } + } + + /** + *

Convert a String to a double, returning + * 0.0d if the conversion fails.

+ * + *

If the string str is null, + * 0.0d is returned.

+ * + *
+     *   NumberUtils.toDouble(null)   = 0.0d
+     *   NumberUtils.toDouble("")     = 0.0d
+     *   NumberUtils.toDouble("1.5")  = 1.5d
+     * 
+ * + * @param str the string to convert, may be null + * @return the double represented by the string, or 0.0d + * if conversion fails + * @since 2.1 + */ + public static double toDouble(String str) { + return toDouble(str, 0.0d); + } + + /** + *

Convert a String to a double, returning a + * default value if the conversion fails.

+ * + *

If the string str is null, the default + * value is returned.

+ * + *
+     *   NumberUtils.toDouble(null, 1.1d)   = 1.1d
+     *   NumberUtils.toDouble("", 1.1d)     = 1.1d
+     *   NumberUtils.toDouble("1.5", 0.0d)  = 1.5d
+     * 
+ * + * @param str the string to convert, may be null + * @param defaultValue the default value + * @return the double represented by the string, or defaultValue + * if conversion fails + * @since 2.1 + */ + public static double toDouble(String str, double defaultValue) { + if (str == null) { + return defaultValue; + } + try { + return Double.parseDouble(str); + } catch (NumberFormatException nfe) { + return defaultValue; + } + } + + //----------------------------------------------------------------------- + /** + *

Convert a String to a byte, returning + * zero if the conversion fails.

+ * + *

If the string is null, zero is returned.

+ * + *
+     *   NumberUtils.toByte(null) = 0
+     *   NumberUtils.toByte("")   = 0
+     *   NumberUtils.toByte("1")  = 1
+     * 
+ * + * @param str the string to convert, may be null + * @return the byte represented by the string, or zero if + * conversion fails + * @since 2.5 + */ + public static byte toByte(String str) { + return toByte(str, (byte) 0); + } + + /** + *

Convert a String to a byte, returning a + * default value if the conversion fails.

+ * + *

If the string is null, the default value is returned.

+ * + *
+     *   NumberUtils.toByte(null, 1) = 1
+     *   NumberUtils.toByte("", 1)   = 1
+     *   NumberUtils.toByte("1", 0)  = 1
+     * 
+ * + * @param str the string to convert, may be null + * @param defaultValue the default value + * @return the byte represented by the string, or the default if conversion fails + * @since 2.5 + */ + public static byte toByte(String str, byte defaultValue) { + if(str == null) { + return defaultValue; + } + try { + return Byte.parseByte(str); + } catch (NumberFormatException nfe) { + return defaultValue; + } + } + + /** + *

Convert a String to a short, returning + * zero if the conversion fails.

+ * + *

If the string is null, zero is returned.

+ * + *
+     *   NumberUtils.toShort(null) = 0
+     *   NumberUtils.toShort("")   = 0
+     *   NumberUtils.toShort("1")  = 1
+     * 
+ * + * @param str the string to convert, may be null + * @return the short represented by the string, or zero if + * conversion fails + * @since 2.5 + */ + public static short toShort(String str) { + return toShort(str, (short) 0); + } + + /** + *

Convert a String to an short, returning a + * default value if the conversion fails.

+ * + *

If the string is null, the default value is returned.

+ * + *
+     *   NumberUtils.toShort(null, 1) = 1
+     *   NumberUtils.toShort("", 1)   = 1
+     *   NumberUtils.toShort("1", 0)  = 1
+     * 
+ * + * @param str the string to convert, may be null + * @param defaultValue the default value + * @return the short represented by the string, or the default if conversion fails + * @since 2.5 + */ + public static short toShort(String str, short defaultValue) { + if(str == null) { + return defaultValue; + } + try { + return Short.parseShort(str); + } catch (NumberFormatException nfe) { + return defaultValue; + } + } + + //----------------------------------------------------------------------- + // must handle Long, Float, Integer, Float, Short, + // BigDecimal, BigInteger and Byte + // useful methods: + // Byte.decode(String) + // Byte.valueOf(String,int radix) + // Byte.valueOf(String) + // Double.valueOf(String) + // Float.valueOf(String) + // Float.valueOf(String) + // Integer.valueOf(String,int radix) + // Integer.valueOf(String) + // Integer.decode(String) + // Integer.getInteger(String) + // Integer.getInteger(String,int val) + // Integer.getInteger(String,Integer val) + // Integer.valueOf(String) + // Double.valueOf(String) + // new Byte(String) + // Long.valueOf(String) + // Long.getLong(String) + // Long.getLong(String,int) + // Long.getLong(String,Integer) + // Long.valueOf(String,int) + // Long.valueOf(String) + // Short.valueOf(String) + // Short.decode(String) + // Short.valueOf(String,int) + // Short.valueOf(String) + // new BigDecimal(String) + // new BigInteger(String) + // new BigInteger(String,int radix) + // Possible inputs: + // 45 45.5 45E7 4.5E7 Hex Oct Binary xxxF xxxD xxxf xxxd + // plus minus everything. Prolly more. A lot are not separable. + + /** + *

Turns a string value into a java.lang.Number.

+ * + *

First, the value is examined for a type qualifier on the end + * ('f','F','d','D','l','L'). If it is found, it starts + * trying to create successively larger types from the type specified + * until one is found that can represent the value.

+ * + *

If a type specifier is not found, it will check for a decimal point + * and then try successively larger types from Integer to + * BigInteger and from Float to + * BigDecimal.

+ * + *

If the string starts with 0x or -0x (lower or upper case), it + * will be interpreted as a hexadecimal integer. Values with leading + * 0's will not be interpreted as octal.

+ * + *

Returns null if the string is null.

+ * + *

This method does not trim the input string, i.e., strings with leading + * or trailing spaces will generate NumberFormatExceptions.

+ * + * @param str String containing a number, may be null + * @return Number created from the string (or null if the input is null) + * @throws NumberFormatException if the value cannot be converted + */ + public static Number createNumber(String str) throws NumberFormatException { + if (str == null) { + return null; + } + if (StringUtils.isBlank(str)) { + throw new NumberFormatException("A blank string is not a valid number"); + } + if (str.startsWith("--")) { + // this is protection for poorness in java.lang.BigDecimal. + // it accepts this as a legal value, but it does not appear + // to be in specification of class. OS X Java parses it to + // a wrong value. + return null; + } + if (str.startsWith("0x") || str.startsWith("-0x") || str.startsWith("0X") || str.startsWith("-0X")) { + return createInteger(str); + } + char lastChar = str.charAt(str.length() - 1); + String mant; + String dec; + String exp; + int decPos = str.indexOf('.'); + int expPos = str.indexOf('e') + str.indexOf('E') + 1; + + if (decPos > -1) { + + if (expPos > -1) { + if (expPos < decPos || expPos > str.length()) { + throw new NumberFormatException(str + " is not a valid number."); + } + dec = str.substring(decPos + 1, expPos); + } else { + dec = str.substring(decPos + 1); + } + mant = str.substring(0, decPos); + } else { + if (expPos > -1) { + if (expPos > str.length()) { + throw new NumberFormatException(str + " is not a valid number."); + } + mant = str.substring(0, expPos); + } else { + mant = str; + } + dec = null; + } + if (!Character.isDigit(lastChar) && lastChar != '.') { + if (expPos > -1 && expPos < str.length() - 1) { + exp = str.substring(expPos + 1, str.length() - 1); + } else { + exp = null; + } + //Requesting a specific type.. + String numeric = str.substring(0, str.length() - 1); + boolean allZeros = isAllZeros(mant) && isAllZeros(exp); + switch (lastChar) { + case 'l' : + case 'L' : + if (dec == null + && exp == null + && (numeric.charAt(0) == '-' && isDigits(numeric.substring(1)) || isDigits(numeric))) { + try { + return createLong(numeric); + } catch (NumberFormatException nfe) { // NOPMD + // Too big for a long + } + return createBigInteger(numeric); + + } + throw new NumberFormatException(str + " is not a valid number."); + case 'f' : + case 'F' : + try { + Float f = NumberUtils.createFloat(numeric); + if (!(f.isInfinite() || (f.floatValue() == 0.0F && !allZeros))) { + //If it's too big for a float or the float value = 0 and the string + //has non-zeros in it, then float does not have the precision we want + return f; + } + + } catch (NumberFormatException nfe) { // NOPMD + // ignore the bad number + } + //$FALL-THROUGH$ + case 'd' : + case 'D' : + try { + Double d = NumberUtils.createDouble(numeric); + if (!(d.isInfinite() || (d.floatValue() == 0.0D && !allZeros))) { + return d; + } + } catch (NumberFormatException nfe) { // NOPMD + // ignore the bad number + } + try { + return createBigDecimal(numeric); + } catch (NumberFormatException e) { // NOPMD + // ignore the bad number + } + //$FALL-THROUGH$ + default : + throw new NumberFormatException(str + " is not a valid number."); + + } + } else { + //User doesn't have a preference on the return type, so let's start + //small and go from there... + if (expPos > -1 && expPos < str.length() - 1) { + exp = str.substring(expPos + 1, str.length()); + } else { + exp = null; + } + if (dec == null && exp == null) { + //Must be an int,long,bigint + try { + return createInteger(str); + } catch (NumberFormatException nfe) { // NOPMD + // ignore the bad number + } + try { + return createLong(str); + } catch (NumberFormatException nfe) { // NOPMD + // ignore the bad number + } + return createBigInteger(str); + + } else { + //Must be a float,double,BigDec + boolean allZeros = isAllZeros(mant) && isAllZeros(exp); + try { + Float f = createFloat(str); + if (!(f.isInfinite() || (f.floatValue() == 0.0F && !allZeros))) { + return f; + } + } catch (NumberFormatException nfe) { // NOPMD + // ignore the bad number + } + try { + Double d = createDouble(str); + if (!(d.isInfinite() || (d.doubleValue() == 0.0D && !allZeros))) { + return d; + } + } catch (NumberFormatException nfe) { // NOPMD + // ignore the bad number + } + + return createBigDecimal(str); + + } + } + } + + /** + *

Utility method for {@link #createNumber(java.lang.String)}.

+ * + *

Returns true if s is null.

+ * + * @param str the String to check + * @return if it is all zeros or null + */ + private static boolean isAllZeros(String str) { + if (str == null) { + return true; + } + for (int i = str.length() - 1; i >= 0; i--) { + if (str.charAt(i) != '0') { + return false; + } + } + return str.length() > 0; + } + + //----------------------------------------------------------------------- + /** + *

Convert a String to a Float.

+ * + *

Returns null if the string is null.

+ * + * @param str a String to convert, may be null + * @return converted Float + * @throws NumberFormatException if the value cannot be converted + */ + public static Float createFloat(String str) { + if (str == null) { + return null; + } + return Float.valueOf(str); + } + + /** + *

Convert a String to a Double.

+ * + *

Returns null if the string is null.

+ * + * @param str a String to convert, may be null + * @return converted Double + * @throws NumberFormatException if the value cannot be converted + */ + public static Double createDouble(String str) { + if (str == null) { + return null; + } + return Double.valueOf(str); + } + + /** + *

Convert a String to a Integer, handling + * hex and octal notations.

+ * + *

Returns null if the string is null.

+ * + * @param str a String to convert, may be null + * @return converted Integer + * @throws NumberFormatException if the value cannot be converted + */ + public static Integer createInteger(String str) { + if (str == null) { + return null; + } + // decode() handles 0xAABD and 0777 (hex and octal) as well. + return Integer.decode(str); + } + + /** + *

Convert a String to a Long; + * since 3.1 it handles hex and octal notations.

+ * + *

Returns null if the string is null.

+ * + * @param str a String to convert, may be null + * @return converted Long + * @throws NumberFormatException if the value cannot be converted + */ + public static Long createLong(String str) { + if (str == null) { + return null; + } + return Long.decode(str); + } + + /** + *

Convert a String to a BigInteger.

+ * + *

Returns null if the string is null.

+ * + * @param str a String to convert, may be null + * @return converted BigInteger + * @throws NumberFormatException if the value cannot be converted + */ + public static BigInteger createBigInteger(String str) { + if (str == null) { + return null; + } + return new BigInteger(str); + } + + /** + *

Convert a String to a BigDecimal.

+ * + *

Returns null if the string is null.

+ * + * @param str a String to convert, may be null + * @return converted BigDecimal + * @throws NumberFormatException if the value cannot be converted + */ + public static BigDecimal createBigDecimal(String str) { + if (str == null) { + return null; + } + // handle JDK1.3.1 bug where "" throws IndexOutOfBoundsException + if (StringUtils.isBlank(str)) { + throw new NumberFormatException("A blank string is not a valid number"); + } + return new BigDecimal(str); + } + + // Min in array + //-------------------------------------------------------------------- + /** + *

Returns the minimum value in an array.

+ * + * @param array an array, must not be null or empty + * @return the minimum value in the array + * @throws IllegalArgumentException if array is null + * @throws IllegalArgumentException if array is empty + */ + public static long min(long[] array) { + // Validates input + if (array == null) { + throw new IllegalArgumentException("The Array must not be null"); + } else if (array.length == 0) { + throw new IllegalArgumentException("Array cannot be empty."); + } + + // Finds and returns min + long min = array[0]; + for (int i = 1; i < array.length; i++) { + if (array[i] < min) { + min = array[i]; + } + } + + return min; + } + + /** + *

Returns the minimum value in an array.

+ * + * @param array an array, must not be null or empty + * @return the minimum value in the array + * @throws IllegalArgumentException if array is null + * @throws IllegalArgumentException if array is empty + */ + public static int min(int[] array) { + // Validates input + if (array == null) { + throw new IllegalArgumentException("The Array must not be null"); + } else if (array.length == 0) { + throw new IllegalArgumentException("Array cannot be empty."); + } + + // Finds and returns min + int min = array[0]; + for (int j = 1; j < array.length; j++) { + if (array[j] < min) { + min = array[j]; + } + } + + return min; + } + + /** + *

Returns the minimum value in an array.

+ * + * @param array an array, must not be null or empty + * @return the minimum value in the array + * @throws IllegalArgumentException if array is null + * @throws IllegalArgumentException if array is empty + */ + public static short min(short[] array) { + // Validates input + if (array == null) { + throw new IllegalArgumentException("The Array must not be null"); + } else if (array.length == 0) { + throw new IllegalArgumentException("Array cannot be empty."); + } + + // Finds and returns min + short min = array[0]; + for (int i = 1; i < array.length; i++) { + if (array[i] < min) { + min = array[i]; + } + } + + return min; + } + + /** + *

Returns the minimum value in an array.

+ * + * @param array an array, must not be null or empty + * @return the minimum value in the array + * @throws IllegalArgumentException if array is null + * @throws IllegalArgumentException if array is empty + */ + public static byte min(byte[] array) { + // Validates input + if (array == null) { + throw new IllegalArgumentException("The Array must not be null"); + } else if (array.length == 0) { + throw new IllegalArgumentException("Array cannot be empty."); + } + + // Finds and returns min + byte min = array[0]; + for (int i = 1; i < array.length; i++) { + if (array[i] < min) { + min = array[i]; + } + } + + return min; + } + + /** + *

Returns the minimum value in an array.

+ * + * @param array an array, must not be null or empty + * @return the minimum value in the array + * @throws IllegalArgumentException if array is null + * @throws IllegalArgumentException if array is empty + * @see IEEE754rUtils#min(double[]) IEEE754rUtils for a version of this method that handles NaN differently + */ + public static double min(double[] array) { + // Validates input + if (array == null) { + throw new IllegalArgumentException("The Array must not be null"); + } else if (array.length == 0) { + throw new IllegalArgumentException("Array cannot be empty."); + } + + // Finds and returns min + double min = array[0]; + for (int i = 1; i < array.length; i++) { + if (Double.isNaN(array[i])) { + return Double.NaN; + } + if (array[i] < min) { + min = array[i]; + } + } + + return min; + } + + /** + *

Returns the minimum value in an array.

+ * + * @param array an array, must not be null or empty + * @return the minimum value in the array + * @throws IllegalArgumentException if array is null + * @throws IllegalArgumentException if array is empty + * @see IEEE754rUtils#min(float[]) IEEE754rUtils for a version of this method that handles NaN differently + */ + public static float min(float[] array) { + // Validates input + if (array == null) { + throw new IllegalArgumentException("The Array must not be null"); + } else if (array.length == 0) { + throw new IllegalArgumentException("Array cannot be empty."); + } + + // Finds and returns min + float min = array[0]; + for (int i = 1; i < array.length; i++) { + if (Float.isNaN(array[i])) { + return Float.NaN; + } + if (array[i] < min) { + min = array[i]; + } + } + + return min; + } + + // Max in array + //-------------------------------------------------------------------- + /** + *

Returns the maximum value in an array.

+ * + * @param array an array, must not be null or empty + * @return the minimum value in the array + * @throws IllegalArgumentException if array is null + * @throws IllegalArgumentException if array is empty + */ + public static long max(long[] array) { + // Validates input + if (array == null) { + throw new IllegalArgumentException("The Array must not be null"); + } else if (array.length == 0) { + throw new IllegalArgumentException("Array cannot be empty."); + } + + // Finds and returns max + long max = array[0]; + for (int j = 1; j < array.length; j++) { + if (array[j] > max) { + max = array[j]; + } + } + + return max; + } + + /** + *

Returns the maximum value in an array.

+ * + * @param array an array, must not be null or empty + * @return the minimum value in the array + * @throws IllegalArgumentException if array is null + * @throws IllegalArgumentException if array is empty + */ + public static int max(int[] array) { + // Validates input + if (array == null) { + throw new IllegalArgumentException("The Array must not be null"); + } else if (array.length == 0) { + throw new IllegalArgumentException("Array cannot be empty."); + } + + // Finds and returns max + int max = array[0]; + for (int j = 1; j < array.length; j++) { + if (array[j] > max) { + max = array[j]; + } + } + + return max; + } + + /** + *

Returns the maximum value in an array.

+ * + * @param array an array, must not be null or empty + * @return the minimum value in the array + * @throws IllegalArgumentException if array is null + * @throws IllegalArgumentException if array is empty + */ + public static short max(short[] array) { + // Validates input + if (array == null) { + throw new IllegalArgumentException("The Array must not be null"); + } else if (array.length == 0) { + throw new IllegalArgumentException("Array cannot be empty."); + } + + // Finds and returns max + short max = array[0]; + for (int i = 1; i < array.length; i++) { + if (array[i] > max) { + max = array[i]; + } + } + + return max; + } + + /** + *

Returns the maximum value in an array.

+ * + * @param array an array, must not be null or empty + * @return the minimum value in the array + * @throws IllegalArgumentException if array is null + * @throws IllegalArgumentException if array is empty + */ + public static byte max(byte[] array) { + // Validates input + if (array == null) { + throw new IllegalArgumentException("The Array must not be null"); + } else if (array.length == 0) { + throw new IllegalArgumentException("Array cannot be empty."); + } + + // Finds and returns max + byte max = array[0]; + for (int i = 1; i < array.length; i++) { + if (array[i] > max) { + max = array[i]; + } + } + + return max; + } + + /** + *

Returns the maximum value in an array.

+ * + * @param array an array, must not be null or empty + * @return the minimum value in the array + * @throws IllegalArgumentException if array is null + * @throws IllegalArgumentException if array is empty + * @see IEEE754rUtils#max(double[]) IEEE754rUtils for a version of this method that handles NaN differently + */ + public static double max(double[] array) { + // Validates input + if (array== null) { + throw new IllegalArgumentException("The Array must not be null"); + } else if (array.length == 0) { + throw new IllegalArgumentException("Array cannot be empty."); + } + + // Finds and returns max + double max = array[0]; + for (int j = 1; j < array.length; j++) { + if (Double.isNaN(array[j])) { + return Double.NaN; + } + if (array[j] > max) { + max = array[j]; + } + } + + return max; + } + + /** + *

Returns the maximum value in an array.

+ * + * @param array an array, must not be null or empty + * @return the minimum value in the array + * @throws IllegalArgumentException if array is null + * @throws IllegalArgumentException if array is empty + * @see IEEE754rUtils#max(float[]) IEEE754rUtils for a version of this method that handles NaN differently + */ + public static float max(float[] array) { + // Validates input + if (array == null) { + throw new IllegalArgumentException("The Array must not be null"); + } else if (array.length == 0) { + throw new IllegalArgumentException("Array cannot be empty."); + } + + // Finds and returns max + float max = array[0]; + for (int j = 1; j < array.length; j++) { + if (Float.isNaN(array[j])) { + return Float.NaN; + } + if (array[j] > max) { + max = array[j]; + } + } + + return max; + } + + // 3 param min + //----------------------------------------------------------------------- + /** + *

Gets the minimum of three long values.

+ * + * @param a value 1 + * @param b value 2 + * @param c value 3 + * @return the smallest of the values + */ + public static long min(long a, long b, long c) { + if (b < a) { + a = b; + } + if (c < a) { + a = c; + } + return a; + } + + /** + *

Gets the minimum of three int values.

+ * + * @param a value 1 + * @param b value 2 + * @param c value 3 + * @return the smallest of the values + */ + public static int min(int a, int b, int c) { + if (b < a) { + a = b; + } + if (c < a) { + a = c; + } + return a; + } + + /** + *

Gets the minimum of three short values.

+ * + * @param a value 1 + * @param b value 2 + * @param c value 3 + * @return the smallest of the values + */ + public static short min(short a, short b, short c) { + if (b < a) { + a = b; + } + if (c < a) { + a = c; + } + return a; + } + + /** + *

Gets the minimum of three byte values.

+ * + * @param a value 1 + * @param b value 2 + * @param c value 3 + * @return the smallest of the values + */ + public static byte min(byte a, byte b, byte c) { + if (b < a) { + a = b; + } + if (c < a) { + a = c; + } + return a; + } + + /** + *

Gets the minimum of three double values.

+ * + *

If any value is NaN, NaN is + * returned. Infinity is handled.

+ * + * @param a value 1 + * @param b value 2 + * @param c value 3 + * @return the smallest of the values + * @see IEEE754rUtils#min(double, double, double) for a version of this method that handles NaN differently + */ + public static double min(double a, double b, double c) { + return Math.min(Math.min(a, b), c); + } + + /** + *

Gets the minimum of three float values.

+ * + *

If any value is NaN, NaN is + * returned. Infinity is handled.

+ * + * @param a value 1 + * @param b value 2 + * @param c value 3 + * @return the smallest of the values + * @see IEEE754rUtils#min(float, float, float) for a version of this method that handles NaN differently + */ + public static float min(float a, float b, float c) { + return Math.min(Math.min(a, b), c); + } + + // 3 param max + //----------------------------------------------------------------------- + /** + *

Gets the maximum of three long values.

+ * + * @param a value 1 + * @param b value 2 + * @param c value 3 + * @return the largest of the values + */ + public static long max(long a, long b, long c) { + if (b > a) { + a = b; + } + if (c > a) { + a = c; + } + return a; + } + + /** + *

Gets the maximum of three int values.

+ * + * @param a value 1 + * @param b value 2 + * @param c value 3 + * @return the largest of the values + */ + public static int max(int a, int b, int c) { + if (b > a) { + a = b; + } + if (c > a) { + a = c; + } + return a; + } + + /** + *

Gets the maximum of three short values.

+ * + * @param a value 1 + * @param b value 2 + * @param c value 3 + * @return the largest of the values + */ + public static short max(short a, short b, short c) { + if (b > a) { + a = b; + } + if (c > a) { + a = c; + } + return a; + } + + /** + *

Gets the maximum of three byte values.

+ * + * @param a value 1 + * @param b value 2 + * @param c value 3 + * @return the largest of the values + */ + public static byte max(byte a, byte b, byte c) { + if (b > a) { + a = b; + } + if (c > a) { + a = c; + } + return a; + } + + /** + *

Gets the maximum of three double values.

+ * + *

If any value is NaN, NaN is + * returned. Infinity is handled.

+ * + * @param a value 1 + * @param b value 2 + * @param c value 3 + * @return the largest of the values + * @see IEEE754rUtils#max(double, double, double) for a version of this method that handles NaN differently + */ + public static double max(double a, double b, double c) { + return Math.max(Math.max(a, b), c); + } + + /** + *

Gets the maximum of three float values.

+ * + *

If any value is NaN, NaN is + * returned. Infinity is handled.

+ * + * @param a value 1 + * @param b value 2 + * @param c value 3 + * @return the largest of the values + * @see IEEE754rUtils#max(float, float, float) for a version of this method that handles NaN differently + */ + public static float max(float a, float b, float c) { + return Math.max(Math.max(a, b), c); + } + + //----------------------------------------------------------------------- + /** + *

Checks whether the String contains only + * digit characters.

+ * + *

Null and empty String will return + * false.

+ * + * @param str the String to check + * @return true if str contains only Unicode numeric + */ + public static boolean isDigits(String str) { + if (StringUtils.isEmpty(str)) { + return false; + } + for (int i = 0; i < str.length(); i++) { + if (!Character.isDigit(str.charAt(i))) { + return false; + } + } + return true; + } + + /** + *

Checks whether the String a valid Java number.

+ * + *

Valid numbers include hexadecimal marked with the 0x + * qualifier, scientific notation and numbers marked with a type + * qualifier (e.g. 123L).

+ * + *

Null and empty String will return + * false.

+ * + * @param str the String to check + * @return true if the string is a correctly formatted number + */ + public static boolean isNumber(String str) { + if (StringUtils.isEmpty(str)) { + return false; + } + char[] chars = str.toCharArray(); + int sz = chars.length; + boolean hasExp = false; + boolean hasDecPoint = false; + boolean allowSigns = false; + boolean foundDigit = false; + // deal with any possible sign up front + int start = (chars[0] == '-') ? 1 : 0; + if (sz > start + 1 && chars[start] == '0' && chars[start + 1] == 'x') { + int i = start + 2; + if (i == sz) { + return false; // str == "0x" + } + // checking hex (it can't be anything else) + for (; i < chars.length; i++) { + if ((chars[i] < '0' || chars[i] > '9') + && (chars[i] < 'a' || chars[i] > 'f') + && (chars[i] < 'A' || chars[i] > 'F')) { + return false; + } + } + return true; + } + sz--; // don't want to loop to the last char, check it afterwords + // for type qualifiers + int i = start; + // loop to the next to last char or to the last char if we need another digit to + // make a valid number (e.g. chars[0..5] = "1234E") + while (i < sz || (i < sz + 1 && allowSigns && !foundDigit)) { + if (chars[i] >= '0' && chars[i] <= '9') { + foundDigit = true; + allowSigns = false; + + } else if (chars[i] == '.') { + if (hasDecPoint || hasExp) { + // two decimal points or dec in exponent + return false; + } + hasDecPoint = true; + } else if (chars[i] == 'e' || chars[i] == 'E') { + // we've already taken care of hex. + if (hasExp) { + // two E's + return false; + } + if (!foundDigit) { + return false; + } + hasExp = true; + allowSigns = true; + } else if (chars[i] == '+' || chars[i] == '-') { + if (!allowSigns) { + return false; + } + allowSigns = false; + foundDigit = false; // we need a digit after the E + } else { + return false; + } + i++; + } + if (i < chars.length) { + if (chars[i] >= '0' && chars[i] <= '9') { + // no type qualifier, OK + return true; + } + if (chars[i] == 'e' || chars[i] == 'E') { + // can't have an E at the last byte + return false; + } + if (chars[i] == '.') { + if (hasDecPoint || hasExp) { + // two decimal points or dec in exponent + return false; + } + // single trailing decimal point after non-exponent is ok + return foundDigit; + } + if (!allowSigns + && (chars[i] == 'd' + || chars[i] == 'D' + || chars[i] == 'f' + || chars[i] == 'F')) { + return foundDigit; + } + if (chars[i] == 'l' + || chars[i] == 'L') { + // not allowing L with an exponent or decimal point + return foundDigit && !hasExp && !hasDecPoint; + } + // last character is illegal + return false; + } + // allowSigns is true iff the val ends in 'E' + // found digit it to make sure weird stuff like '.' and '1E-' doesn't pass + return !allowSigns && foundDigit; + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/math/NumberUtilsTest.java b/ApacheCommonsLang/org/apache/commons/lang3/math/NumberUtilsTest.java new file mode 100644 index 0000000..b7f7bc2 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/math/NumberUtilsTest.java @@ -0,0 +1,1279 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.math; + +import static org.apache.commons.lang3.JavaVersion.JAVA_1_3; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Modifier; +import java.math.BigDecimal; +import java.math.BigInteger; + +import org.apache.commons.lang3.SystemUtils; +import org.junit.Test; + +/** + * Unit tests {@link org.apache.commons.lang3.math.NumberUtils}. + * + * @version $Id: NumberUtilsTest.java 1309912 2012-04-05 15:53:18Z ggregory $ + */ +public class NumberUtilsTest { + + //----------------------------------------------------------------------- + @Test + public void testConstructor() { + assertNotNull(new NumberUtils()); + Constructor[] cons = NumberUtils.class.getDeclaredConstructors(); + assertEquals(1, cons.length); + assertTrue(Modifier.isPublic(cons[0].getModifiers())); + assertTrue(Modifier.isPublic(NumberUtils.class.getModifiers())); + assertFalse(Modifier.isFinal(NumberUtils.class.getModifiers())); + } + + //--------------------------------------------------------------------- + + /** + * Test for {@link NumberUtils#toInt(String)}. + */ + @Test + public void testToIntString() { + assertTrue("toInt(String) 1 failed", NumberUtils.toInt("12345") == 12345); + assertTrue("toInt(String) 2 failed", NumberUtils.toInt("abc") == 0); + assertTrue("toInt(empty) failed", NumberUtils.toInt("") == 0); + assertTrue("toInt(null) failed", NumberUtils.toInt(null) == 0); + } + + /** + * Test for {@link NumberUtils#toInt(String, int)}. + */ + @Test + public void testToIntStringI() { + assertTrue("toInt(String,int) 1 failed", NumberUtils.toInt("12345", 5) == 12345); + assertTrue("toInt(String,int) 2 failed", NumberUtils.toInt("1234.5", 5) == 5); + } + + /** + * Test for {@link NumberUtils#toLong(String)}. + */ + @Test + public void testToLongString() { + assertTrue("toLong(String) 1 failed", NumberUtils.toLong("12345") == 12345l); + assertTrue("toLong(String) 2 failed", NumberUtils.toLong("abc") == 0l); + assertTrue("toLong(String) 3 failed", NumberUtils.toLong("1L") == 0l); + assertTrue("toLong(String) 4 failed", NumberUtils.toLong("1l") == 0l); + assertTrue("toLong(Long.MAX_VALUE) failed", NumberUtils.toLong(Long.MAX_VALUE+"") == Long.MAX_VALUE); + assertTrue("toLong(Long.MIN_VALUE) failed", NumberUtils.toLong(Long.MIN_VALUE+"") == Long.MIN_VALUE); + assertTrue("toLong(empty) failed", NumberUtils.toLong("") == 0l); + assertTrue("toLong(null) failed", NumberUtils.toLong(null) == 0l); + } + + /** + * Test for {@link NumberUtils#toLong(String, long)}. + */ + @Test + public void testToLongStringL() { + assertTrue("toLong(String,long) 1 failed", NumberUtils.toLong("12345", 5l) == 12345l); + assertTrue("toLong(String,long) 2 failed", NumberUtils.toLong("1234.5", 5l) == 5l); + } + + /** + * Test for {@link NumberUtils#toFloat(String)}. + */ + @Test + public void testToFloatString() { + assertTrue("toFloat(String) 1 failed", NumberUtils.toFloat("-1.2345") == -1.2345f); + assertTrue("toFloat(String) 2 failed", NumberUtils.toFloat("1.2345") == 1.2345f); + assertTrue("toFloat(String) 3 failed", NumberUtils.toFloat("abc") == 0.0f); + assertTrue("toFloat(Float.MAX_VALUE) failed", NumberUtils.toFloat(Float.MAX_VALUE+"") == Float.MAX_VALUE); + assertTrue("toFloat(Float.MIN_VALUE) failed", NumberUtils.toFloat(Float.MIN_VALUE+"") == Float.MIN_VALUE); + assertTrue("toFloat(empty) failed", NumberUtils.toFloat("") == 0.0f); + assertTrue("toFloat(null) failed", NumberUtils.toFloat(null) == 0.0f); + } + + /** + * Test for {@link NumberUtils#toFloat(String, float)}. + */ + @Test + public void testToFloatStringF() { + assertTrue("toFloat(String,int) 1 failed", NumberUtils.toFloat("1.2345", 5.1f) == 1.2345f); + assertTrue("toFloat(String,int) 2 failed", NumberUtils.toFloat("a", 5.0f) == 5.0f); + } + + /** + * Test for {@link NumberUtils#toDouble(String)}. + */ + @Test + public void testStringToDoubleString() { + assertTrue("toDouble(String) 1 failed", NumberUtils.toDouble("-1.2345") == -1.2345d); + assertTrue("toDouble(String) 2 failed", NumberUtils.toDouble("1.2345") == 1.2345d); + assertTrue("toDouble(String) 3 failed", NumberUtils.toDouble("abc") == 0.0d); + assertTrue("toDouble(Double.MAX_VALUE) failed", NumberUtils.toDouble(Double.MAX_VALUE+"") == Double.MAX_VALUE); + assertTrue("toDouble(Double.MIN_VALUE) failed", NumberUtils.toDouble(Double.MIN_VALUE+"") == Double.MIN_VALUE); + assertTrue("toDouble(empty) failed", NumberUtils.toDouble("") == 0.0d); + assertTrue("toDouble(null) failed", NumberUtils.toDouble(null) == 0.0d); + } + + /** + * Test for {@link NumberUtils#toDouble(String, double)}. + */ + @Test + public void testStringToDoubleStringD() { + assertTrue("toDouble(String,int) 1 failed", NumberUtils.toDouble("1.2345", 5.1d) == 1.2345d); + assertTrue("toDouble(String,int) 2 failed", NumberUtils.toDouble("a", 5.0d) == 5.0d); + } + + /** + * Test for {@link NumberUtils#toByte(String)}. + */ + @Test + public void testToByteString() { + assertTrue("toByte(String) 1 failed", NumberUtils.toByte("123") == 123); + assertTrue("toByte(String) 2 failed", NumberUtils.toByte("abc") == 0); + assertTrue("toByte(empty) failed", NumberUtils.toByte("") == 0); + assertTrue("toByte(null) failed", NumberUtils.toByte(null) == 0); + } + + /** + * Test for {@link NumberUtils#toByte(String, byte)}. + */ + @Test + public void testToByteStringI() { + assertTrue("toByte(String,byte) 1 failed", NumberUtils.toByte("123", (byte) 5) == 123); + assertTrue("toByte(String,byte) 2 failed", NumberUtils.toByte("12.3", (byte) 5) == 5); + } + + /** + * Test for {@link NumberUtils#toShort(String)}. + */ + @Test + public void testToShortString() { + assertTrue("toShort(String) 1 failed", NumberUtils.toShort("12345") == 12345); + assertTrue("toShort(String) 2 failed", NumberUtils.toShort("abc") == 0); + assertTrue("toShort(empty) failed", NumberUtils.toShort("") == 0); + assertTrue("toShort(null) failed", NumberUtils.toShort(null) == 0); + } + + /** + * Test for {@link NumberUtils#toShort(String, short)}. + */ + @Test + public void testToShortStringI() { + assertTrue("toShort(String,short) 1 failed", NumberUtils.toShort("12345", (short) 5) == 12345); + assertTrue("toShort(String,short) 2 failed", NumberUtils.toShort("1234.5", (short) 5) == 5); + } + + @Test + public void testCreateNumber() { + // a lot of things can go wrong + assertEquals("createNumber(String) 1 failed", Float.valueOf("1234.5"), NumberUtils.createNumber("1234.5")); + assertEquals("createNumber(String) 2 failed", Integer.valueOf("12345"), NumberUtils.createNumber("12345")); + assertEquals("createNumber(String) 3 failed", Double.valueOf("1234.5"), NumberUtils.createNumber("1234.5D")); + assertEquals("createNumber(String) 3 failed", Double.valueOf("1234.5"), NumberUtils.createNumber("1234.5d")); + assertEquals("createNumber(String) 4 failed", Float.valueOf("1234.5"), NumberUtils.createNumber("1234.5F")); + assertEquals("createNumber(String) 4 failed", Float.valueOf("1234.5"), NumberUtils.createNumber("1234.5f")); + assertEquals("createNumber(String) 5 failed", Long.valueOf(Integer.MAX_VALUE + 1L), NumberUtils.createNumber("" + + (Integer.MAX_VALUE + 1L))); + assertEquals("createNumber(String) 6 failed", Long.valueOf(12345), NumberUtils.createNumber("12345L")); + assertEquals("createNumber(String) 6 failed", Long.valueOf(12345), NumberUtils.createNumber("12345l")); + assertEquals("createNumber(String) 7 failed", Float.valueOf("-1234.5"), NumberUtils.createNumber("-1234.5")); + assertEquals("createNumber(String) 8 failed", Integer.valueOf("-12345"), NumberUtils.createNumber("-12345")); + assertTrue("createNumber(String) 9a failed", 0xFADE == NumberUtils.createNumber("0xFADE").intValue()); + assertTrue("createNumber(String) 9b failed", 0xFADE == NumberUtils.createNumber("0Xfade").intValue()); + assertTrue("createNumber(String) 10a failed", -0xFADE == NumberUtils.createNumber("-0xFADE").intValue()); + assertTrue("createNumber(String) 10b failed", -0xFADE == NumberUtils.createNumber("-0Xfade").intValue()); + assertEquals("createNumber(String) 11 failed", Double.valueOf("1.1E200"), NumberUtils.createNumber("1.1E200")); + assertEquals("createNumber(String) 12 failed", Float.valueOf("1.1E20"), NumberUtils.createNumber("1.1E20")); + assertEquals("createNumber(String) 13 failed", Double.valueOf("-1.1E200"), NumberUtils.createNumber("-1.1E200")); + assertEquals("createNumber(String) 14 failed", Double.valueOf("1.1E-200"), NumberUtils.createNumber("1.1E-200")); + assertEquals("createNumber(null) failed", null, NumberUtils.createNumber(null)); + assertEquals("createNumber(String) failed", new BigInteger("12345678901234567890"), NumberUtils + .createNumber("12345678901234567890L")); + + // jdk 1.2 doesn't support this. unsure about jdk 1.2.2 + if (SystemUtils.isJavaVersionAtLeast(JAVA_1_3)) { + assertEquals("createNumber(String) 15 failed", new BigDecimal("1.1E-700"), NumberUtils + .createNumber("1.1E-700F")); + } + assertEquals("createNumber(String) 16 failed", Long.valueOf("10" + Integer.MAX_VALUE), NumberUtils + .createNumber("10" + Integer.MAX_VALUE + "L")); + assertEquals("createNumber(String) 17 failed", Long.valueOf("10" + Integer.MAX_VALUE), NumberUtils + .createNumber("10" + Integer.MAX_VALUE)); + assertEquals("createNumber(String) 18 failed", new BigInteger("10" + Long.MAX_VALUE), NumberUtils + .createNumber("10" + Long.MAX_VALUE)); + + // LANG-521 + assertEquals("createNumber(String) LANG-521 failed", Float.valueOf("2."), NumberUtils.createNumber("2.")); + + // LANG-638 + assertFalse("createNumber(String) succeeded", checkCreateNumber("1eE")); + + // LANG-693 + assertEquals("createNumber(String) LANG-693 failed", Double.valueOf(Double.MAX_VALUE), NumberUtils + .createNumber("" + Double.MAX_VALUE)); + } + + @Test + public void testCreateFloat() { + assertEquals("createFloat(String) failed", Float.valueOf("1234.5"), NumberUtils.createFloat("1234.5")); + assertEquals("createFloat(null) failed", null, NumberUtils.createFloat(null)); + this.testCreateFloatFailure(""); + this.testCreateFloatFailure(" "); + this.testCreateFloatFailure("\b\t\n\f\r"); + // Funky whitespaces + this.testCreateFloatFailure("\u00A0\uFEFF\u000B\u000C\u001C\u001D\u001E\u001F"); + } + + protected void testCreateFloatFailure(String str) { + try { + Float value = NumberUtils.createFloat(str); + fail("createFloat(blank) failed: " + value); + } catch (NumberFormatException ex) { + // empty + } + } + + @Test + public void testCreateDouble() { + assertEquals("createDouble(String) failed", Double.valueOf("1234.5"), NumberUtils.createDouble("1234.5")); + assertEquals("createDouble(null) failed", null, NumberUtils.createDouble(null)); + this.testCreateDoubleFailure(""); + this.testCreateDoubleFailure(" "); + this.testCreateDoubleFailure("\b\t\n\f\r"); + // Funky whitespaces + this.testCreateDoubleFailure("\u00A0\uFEFF\u000B\u000C\u001C\u001D\u001E\u001F"); + } + + protected void testCreateDoubleFailure(String str) { + try { + Double value = NumberUtils.createDouble(str); + fail("createDouble(blank) failed: " + value); + } catch (NumberFormatException ex) { + // empty + } + } + + @Test + public void testCreateInteger() { + assertEquals("createInteger(String) failed", Integer.valueOf("12345"), NumberUtils.createInteger("12345")); + assertEquals("createInteger(null) failed", null, NumberUtils.createInteger(null)); + this.testCreateIntegerFailure(""); + this.testCreateIntegerFailure(" "); + this.testCreateIntegerFailure("\b\t\n\f\r"); + // Funky whitespaces + this.testCreateIntegerFailure("\u00A0\uFEFF\u000B\u000C\u001C\u001D\u001E\u001F"); + } + + protected void testCreateIntegerFailure(String str) { + try { + Integer value = NumberUtils.createInteger(str); + fail("createInteger(blank) failed: " + value); + } catch (NumberFormatException ex) { + // empty + } + } + + @Test + public void testCreateLong() { + assertEquals("createLong(String) failed", Long.valueOf("12345"), NumberUtils.createLong("12345")); + assertEquals("createLong(null) failed", null, NumberUtils.createLong(null)); + this.testCreateLongFailure(""); + this.testCreateLongFailure(" "); + this.testCreateLongFailure("\b\t\n\f\r"); + // Funky whitespaces + this.testCreateLongFailure("\u00A0\uFEFF\u000B\u000C\u001C\u001D\u001E\u001F"); + } + + protected void testCreateLongFailure(String str) { + try { + Long value = NumberUtils.createLong(str); + fail("createLong(blank) failed: " + value); + } catch (NumberFormatException ex) { + // empty + } + } + + @Test + public void testCreateBigInteger() { + assertEquals("createBigInteger(String) failed", new BigInteger("12345"), NumberUtils.createBigInteger("12345")); + assertEquals("createBigInteger(null) failed", null, NumberUtils.createBigInteger(null)); + this.testCreateBigIntegerFailure(""); + this.testCreateBigIntegerFailure(" "); + this.testCreateBigIntegerFailure("\b\t\n\f\r"); + // Funky whitespaces + this.testCreateBigIntegerFailure("\u00A0\uFEFF\u000B\u000C\u001C\u001D\u001E\u001F"); + } + + protected void testCreateBigIntegerFailure(String str) { + try { + BigInteger value = NumberUtils.createBigInteger(str); + fail("createBigInteger(blank) failed: " + value); + } catch (NumberFormatException ex) { + // empty + } + } + + @Test + public void testCreateBigDecimal() { + assertEquals("createBigDecimal(String) failed", new BigDecimal("1234.5"), NumberUtils.createBigDecimal("1234.5")); + assertEquals("createBigDecimal(null) failed", null, NumberUtils.createBigDecimal(null)); + this.testCreateBigDecimalFailure(""); + this.testCreateBigDecimalFailure(" "); + this.testCreateBigDecimalFailure("\b\t\n\f\r"); + // Funky whitespaces + this.testCreateBigDecimalFailure("\u00A0\uFEFF\u000B\u000C\u001C\u001D\u001E\u001F"); + } + + protected void testCreateBigDecimalFailure(String str) { + try { + BigDecimal value = NumberUtils.createBigDecimal(str); + fail("createBigDecimal(blank) failed: " + value); + } catch (NumberFormatException ex) { + // empty + } + } + + // min/max tests + // ---------------------------------------------------------------------- + @Test(expected = IllegalArgumentException.class) + public void testMinLong_nullArray() { + NumberUtils.min((long[]) null); + } + + @Test(expected = IllegalArgumentException.class) + public void testMinLong_emptyArray() { + NumberUtils.min(new long[0]); + } + + @Test + public void testMinLong() { + assertEquals( + "min(long[]) failed for array length 1", + 5, + NumberUtils.min(new long[] { 5 })); + + assertEquals( + "min(long[]) failed for array length 2", + 6, + NumberUtils.min(new long[] { 6, 9 })); + + assertEquals(-10, NumberUtils.min(new long[] { -10, -5, 0, 5, 10 })); + assertEquals(-10, NumberUtils.min(new long[] { -5, 0, -10, 5, 10 })); + } + + @Test(expected = IllegalArgumentException.class) + public void testMinInt_nullArray() { + NumberUtils.min((int[]) null); + } + + @Test(expected = IllegalArgumentException.class) + public void testMinInt_emptyArray() { + NumberUtils.min(new int[0]); + } + + @Test + public void testMinInt() { + assertEquals( + "min(int[]) failed for array length 1", + 5, + NumberUtils.min(new int[] { 5 })); + + assertEquals( + "min(int[]) failed for array length 2", + 6, + NumberUtils.min(new int[] { 6, 9 })); + + assertEquals(-10, NumberUtils.min(new int[] { -10, -5, 0, 5, 10 })); + assertEquals(-10, NumberUtils.min(new int[] { -5, 0, -10, 5, 10 })); + } + + @Test(expected = IllegalArgumentException.class) + public void testMinShort_nullArray() { + NumberUtils.min((short[]) null); + } + + @Test(expected = IllegalArgumentException.class) + public void testMinShort_emptyArray() { + NumberUtils.min(new short[0]); + } + + @Test + public void testMinShort() { + assertEquals( + "min(short[]) failed for array length 1", + 5, + NumberUtils.min(new short[] { 5 })); + + assertEquals( + "min(short[]) failed for array length 2", + 6, + NumberUtils.min(new short[] { 6, 9 })); + + assertEquals(-10, NumberUtils.min(new short[] { -10, -5, 0, 5, 10 })); + assertEquals(-10, NumberUtils.min(new short[] { -5, 0, -10, 5, 10 })); + } + + @Test(expected = IllegalArgumentException.class) + public void testMinByte_nullArray() { + NumberUtils.min((byte[]) null); + } + + @Test(expected = IllegalArgumentException.class) + public void testMinByte_emptyArray() { + NumberUtils.min(new byte[0]); + } + + @Test + public void testMinByte() { + assertEquals( + "min(byte[]) failed for array length 1", + 5, + NumberUtils.min(new byte[] { 5 })); + + assertEquals( + "min(byte[]) failed for array length 2", + 6, + NumberUtils.min(new byte[] { 6, 9 })); + + assertEquals(-10, NumberUtils.min(new byte[] { -10, -5, 0, 5, 10 })); + assertEquals(-10, NumberUtils.min(new byte[] { -5, 0, -10, 5, 10 })); + } + + @Test(expected = IllegalArgumentException.class) + public void testMinDouble_nullArray() { + NumberUtils.min((double[]) null); + } + + @Test(expected = IllegalArgumentException.class) + public void testMinDouble_emptyArray() { + NumberUtils.min(new double[0]); + } + + @Test + public void testMinDouble() { + assertEquals( + "min(double[]) failed for array length 1", + 5.12, + NumberUtils.min(new double[] { 5.12 }), + 0); + + assertEquals( + "min(double[]) failed for array length 2", + 6.23, + NumberUtils.min(new double[] { 6.23, 9.34 }), + 0); + + assertEquals( + "min(double[]) failed for array length 5", + -10.45, + NumberUtils.min(new double[] { -10.45, -5.56, 0, 5.67, 10.78 }), + 0); + assertEquals(-10, NumberUtils.min(new double[] { -10, -5, 0, 5, 10 }), 0.0001); + assertEquals(-10, NumberUtils.min(new double[] { -5, 0, -10, 5, 10 }), 0.0001); + } + + @Test(expected = IllegalArgumentException.class) + public void testMinFloat_nullArray() { + NumberUtils.min((float[]) null); + } + + @Test(expected = IllegalArgumentException.class) + public void testMinFloat_emptyArray() { + NumberUtils.min(new float[0]); + } + + @Test + public void testMinFloat() { + assertEquals( + "min(float[]) failed for array length 1", + 5.9f, + NumberUtils.min(new float[] { 5.9f }), + 0); + + assertEquals( + "min(float[]) failed for array length 2", + 6.8f, + NumberUtils.min(new float[] { 6.8f, 9.7f }), + 0); + + assertEquals( + "min(float[]) failed for array length 5", + -10.6f, + NumberUtils.min(new float[] { -10.6f, -5.5f, 0, 5.4f, 10.3f }), + 0); + assertEquals(-10, NumberUtils.min(new float[] { -10, -5, 0, 5, 10 }), 0.0001f); + assertEquals(-10, NumberUtils.min(new float[] { -5, 0, -10, 5, 10 }), 0.0001f); + } + + @Test(expected = IllegalArgumentException.class) + public void testMaxLong_nullArray() { + NumberUtils.max((long[]) null); + } + + @Test(expected = IllegalArgumentException.class) + public void testMaxLong_emptyArray() { + NumberUtils.max(new long[0]); + } + + @Test + public void testMaxLong() { + assertEquals( + "max(long[]) failed for array length 1", + 5, + NumberUtils.max(new long[] { 5 })); + + assertEquals( + "max(long[]) failed for array length 2", + 9, + NumberUtils.max(new long[] { 6, 9 })); + + assertEquals( + "max(long[]) failed for array length 5", + 10, + NumberUtils.max(new long[] { -10, -5, 0, 5, 10 })); + assertEquals(10, NumberUtils.max(new long[] { -10, -5, 0, 5, 10 })); + assertEquals(10, NumberUtils.max(new long[] { -5, 0, 10, 5, -10 })); + } + + @Test(expected = IllegalArgumentException.class) + public void testMaxInt_nullArray() { + NumberUtils.max((int[]) null); + } + + @Test(expected = IllegalArgumentException.class) + public void testMaxInt_emptyArray() { + NumberUtils.max(new int[0]); + } + + @Test + public void testMaxInt() { + assertEquals( + "max(int[]) failed for array length 1", + 5, + NumberUtils.max(new int[] { 5 })); + + assertEquals( + "max(int[]) failed for array length 2", + 9, + NumberUtils.max(new int[] { 6, 9 })); + + assertEquals( + "max(int[]) failed for array length 5", + 10, + NumberUtils.max(new int[] { -10, -5, 0, 5, 10 })); + assertEquals(10, NumberUtils.max(new int[] { -10, -5, 0, 5, 10 })); + assertEquals(10, NumberUtils.max(new int[] { -5, 0, 10, 5, -10 })); + } + + @Test(expected = IllegalArgumentException.class) + public void testMaxShort_nullArray() { + NumberUtils.max((short[]) null); + } + + @Test(expected = IllegalArgumentException.class) + public void testMaxShort_emptyArray() { + NumberUtils.max(new short[0]); + } + + @Test + public void testMaxShort() { + assertEquals( + "max(short[]) failed for array length 1", + 5, + NumberUtils.max(new short[] { 5 })); + + assertEquals( + "max(short[]) failed for array length 2", + 9, + NumberUtils.max(new short[] { 6, 9 })); + + assertEquals( + "max(short[]) failed for array length 5", + 10, + NumberUtils.max(new short[] { -10, -5, 0, 5, 10 })); + assertEquals(10, NumberUtils.max(new short[] { -10, -5, 0, 5, 10 })); + assertEquals(10, NumberUtils.max(new short[] { -5, 0, 10, 5, -10 })); + } + + @Test(expected = IllegalArgumentException.class) + public void testMaxByte_nullArray() { + NumberUtils.max((byte[]) null); + } + + @Test(expected = IllegalArgumentException.class) + public void testMaxByte_emptyArray() { + NumberUtils.max(new byte[0]); + } + + @Test + public void testMaxByte() { + assertEquals( + "max(byte[]) failed for array length 1", + 5, + NumberUtils.max(new byte[] { 5 })); + + assertEquals( + "max(byte[]) failed for array length 2", + 9, + NumberUtils.max(new byte[] { 6, 9 })); + + assertEquals( + "max(byte[]) failed for array length 5", + 10, + NumberUtils.max(new byte[] { -10, -5, 0, 5, 10 })); + assertEquals(10, NumberUtils.max(new byte[] { -10, -5, 0, 5, 10 })); + assertEquals(10, NumberUtils.max(new byte[] { -5, 0, 10, 5, -10 })); + } + + @Test(expected = IllegalArgumentException.class) + public void testMaxDouble_nullArray() { + NumberUtils.max((double[]) null); + } + + @Test(expected = IllegalArgumentException.class) + public void testMaxDouble_emptyArray() { + NumberUtils.max(new double[0]); + } + + @Test + public void testMaxDouble() { + final double[] d = null; + try { + NumberUtils.max(d); + fail("No exception was thrown for null input."); + } catch (IllegalArgumentException ex) {} + + try { + NumberUtils.max(new double[0]); + fail("No exception was thrown for empty input."); + } catch (IllegalArgumentException ex) {} + + assertEquals( + "max(double[]) failed for array length 1", + 5.1f, + NumberUtils.max(new double[] { 5.1f }), + 0); + + assertEquals( + "max(double[]) failed for array length 2", + 9.2f, + NumberUtils.max(new double[] { 6.3f, 9.2f }), + 0); + + assertEquals( + "max(double[]) failed for float length 5", + 10.4f, + NumberUtils.max(new double[] { -10.5f, -5.6f, 0, 5.7f, 10.4f }), + 0); + assertEquals(10, NumberUtils.max(new double[] { -10, -5, 0, 5, 10 }), 0.0001); + assertEquals(10, NumberUtils.max(new double[] { -5, 0, 10, 5, -10 }), 0.0001); + } + + @Test(expected = IllegalArgumentException.class) + public void testMaxFloat_nullArray() { + NumberUtils.max((float[]) null); + } + + @Test(expected = IllegalArgumentException.class) + public void testMaxFloat_emptyArray() { + NumberUtils.max(new float[0]); + } + + @Test + public void testMaxFloat() { + assertEquals( + "max(float[]) failed for array length 1", + 5.1f, + NumberUtils.max(new float[] { 5.1f }), + 0); + + assertEquals( + "max(float[]) failed for array length 2", + 9.2f, + NumberUtils.max(new float[] { 6.3f, 9.2f }), + 0); + + assertEquals( + "max(float[]) failed for float length 5", + 10.4f, + NumberUtils.max(new float[] { -10.5f, -5.6f, 0, 5.7f, 10.4f }), + 0); + assertEquals(10, NumberUtils.max(new float[] { -10, -5, 0, 5, 10 }), 0.0001f); + assertEquals(10, NumberUtils.max(new float[] { -5, 0, 10, 5, -10 }), 0.0001f); + } + + @Test + public void testMinimumLong() { + assertEquals("minimum(long,long,long) 1 failed", 12345L, NumberUtils.min(12345L, 12345L + 1L, 12345L + 2L)); + assertEquals("minimum(long,long,long) 2 failed", 12345L, NumberUtils.min(12345L + 1L, 12345L, 12345 + 2L)); + assertEquals("minimum(long,long,long) 3 failed", 12345L, NumberUtils.min(12345L + 1L, 12345L + 2L, 12345L)); + assertEquals("minimum(long,long,long) 4 failed", 12345L, NumberUtils.min(12345L + 1L, 12345L, 12345L)); + assertEquals("minimum(long,long,long) 5 failed", 12345L, NumberUtils.min(12345L, 12345L, 12345L)); + } + + @Test + public void testMinimumInt() { + assertEquals("minimum(int,int,int) 1 failed", 12345, NumberUtils.min(12345, 12345 + 1, 12345 + 2)); + assertEquals("minimum(int,int,int) 2 failed", 12345, NumberUtils.min(12345 + 1, 12345, 12345 + 2)); + assertEquals("minimum(int,int,int) 3 failed", 12345, NumberUtils.min(12345 + 1, 12345 + 2, 12345)); + assertEquals("minimum(int,int,int) 4 failed", 12345, NumberUtils.min(12345 + 1, 12345, 12345)); + assertEquals("minimum(int,int,int) 5 failed", 12345, NumberUtils.min(12345, 12345, 12345)); + } + + @Test + public void testMinimumShort() { + short low = 1234; + short mid = 1234 + 1; + short high = 1234 + 2; + assertEquals("minimum(short,short,short) 1 failed", low, NumberUtils.min(low, mid, high)); + assertEquals("minimum(short,short,short) 1 failed", low, NumberUtils.min(mid, low, high)); + assertEquals("minimum(short,short,short) 1 failed", low, NumberUtils.min(mid, high, low)); + assertEquals("minimum(short,short,short) 1 failed", low, NumberUtils.min(low, mid, low)); + } + + @Test + public void testMinimumByte() { + byte low = 123; + byte mid = 123 + 1; + byte high = 123 + 2; + assertEquals("minimum(byte,byte,byte) 1 failed", low, NumberUtils.min(low, mid, high)); + assertEquals("minimum(byte,byte,byte) 1 failed", low, NumberUtils.min(mid, low, high)); + assertEquals("minimum(byte,byte,byte) 1 failed", low, NumberUtils.min(mid, high, low)); + assertEquals("minimum(byte,byte,byte) 1 failed", low, NumberUtils.min(low, mid, low)); + } + + @Test + public void testMinimumDouble() { + double low = 12.3; + double mid = 12.3 + 1; + double high = 12.3 + 2; + assertEquals(low, NumberUtils.min(low, mid, high), 0.0001); + assertEquals(low, NumberUtils.min(mid, low, high), 0.0001); + assertEquals(low, NumberUtils.min(mid, high, low), 0.0001); + assertEquals(low, NumberUtils.min(low, mid, low), 0.0001); + assertEquals(mid, NumberUtils.min(high, mid, high), 0.0001); + } + + @Test + public void testMinimumFloat() { + float low = 12.3f; + float mid = 12.3f + 1; + float high = 12.3f + 2; + assertEquals(low, NumberUtils.min(low, mid, high), 0.0001f); + assertEquals(low, NumberUtils.min(mid, low, high), 0.0001f); + assertEquals(low, NumberUtils.min(mid, high, low), 0.0001f); + assertEquals(low, NumberUtils.min(low, mid, low), 0.0001f); + assertEquals(mid, NumberUtils.min(high, mid, high), 0.0001f); + } + + @Test + public void testMaximumLong() { + assertEquals("maximum(long,long,long) 1 failed", 12345L, NumberUtils.max(12345L, 12345L - 1L, 12345L - 2L)); + assertEquals("maximum(long,long,long) 2 failed", 12345L, NumberUtils.max(12345L - 1L, 12345L, 12345L - 2L)); + assertEquals("maximum(long,long,long) 3 failed", 12345L, NumberUtils.max(12345L - 1L, 12345L - 2L, 12345L)); + assertEquals("maximum(long,long,long) 4 failed", 12345L, NumberUtils.max(12345L - 1L, 12345L, 12345L)); + assertEquals("maximum(long,long,long) 5 failed", 12345L, NumberUtils.max(12345L, 12345L, 12345L)); + } + + @Test + public void testMaximumInt() { + assertEquals("maximum(int,int,int) 1 failed", 12345, NumberUtils.max(12345, 12345 - 1, 12345 - 2)); + assertEquals("maximum(int,int,int) 2 failed", 12345, NumberUtils.max(12345 - 1, 12345, 12345 - 2)); + assertEquals("maximum(int,int,int) 3 failed", 12345, NumberUtils.max(12345 - 1, 12345 - 2, 12345)); + assertEquals("maximum(int,int,int) 4 failed", 12345, NumberUtils.max(12345 - 1, 12345, 12345)); + assertEquals("maximum(int,int,int) 5 failed", 12345, NumberUtils.max(12345, 12345, 12345)); + } + + @Test + public void testMaximumShort() { + short low = 1234; + short mid = 1234 + 1; + short high = 1234 + 2; + assertEquals("maximum(short,short,short) 1 failed", high, NumberUtils.max(low, mid, high)); + assertEquals("maximum(short,short,short) 1 failed", high, NumberUtils.max(mid, low, high)); + assertEquals("maximum(short,short,short) 1 failed", high, NumberUtils.max(mid, high, low)); + assertEquals("maximum(short,short,short) 1 failed", high, NumberUtils.max(high, mid, high)); + } + + @Test + public void testMaximumByte() { + byte low = 123; + byte mid = 123 + 1; + byte high = 123 + 2; + assertEquals("maximum(byte,byte,byte) 1 failed", high, NumberUtils.max(low, mid, high)); + assertEquals("maximum(byte,byte,byte) 1 failed", high, NumberUtils.max(mid, low, high)); + assertEquals("maximum(byte,byte,byte) 1 failed", high, NumberUtils.max(mid, high, low)); + assertEquals("maximum(byte,byte,byte) 1 failed", high, NumberUtils.max(high, mid, high)); + } + + @Test + public void testMaximumDouble() { + double low = 12.3; + double mid = 12.3 + 1; + double high = 12.3 + 2; + assertEquals(high, NumberUtils.max(low, mid, high), 0.0001); + assertEquals(high, NumberUtils.max(mid, low, high), 0.0001); + assertEquals(high, NumberUtils.max(mid, high, low), 0.0001); + assertEquals(mid, NumberUtils.max(low, mid, low), 0.0001); + assertEquals(high, NumberUtils.max(high, mid, high), 0.0001); + } + + @Test + public void testMaximumFloat() { + float low = 12.3f; + float mid = 12.3f + 1; + float high = 12.3f + 2; + assertEquals(high, NumberUtils.max(low, mid, high), 0.0001f); + assertEquals(high, NumberUtils.max(mid, low, high), 0.0001f); + assertEquals(high, NumberUtils.max(mid, high, low), 0.0001f); + assertEquals(mid, NumberUtils.max(low, mid, low), 0.0001f); + assertEquals(high, NumberUtils.max(high, mid, high), 0.0001f); + } + + // Testing JDK against old Lang functionality + @Test + public void testCompareDouble() { + assertTrue(Double.compare(Double.NaN, Double.NaN) == 0); + assertTrue(Double.compare(Double.NaN, Double.POSITIVE_INFINITY) == +1); + assertTrue(Double.compare(Double.NaN, Double.MAX_VALUE) == +1); + assertTrue(Double.compare(Double.NaN, 1.2d) == +1); + assertTrue(Double.compare(Double.NaN, 0.0d) == +1); + assertTrue(Double.compare(Double.NaN, -0.0d) == +1); + assertTrue(Double.compare(Double.NaN, -1.2d) == +1); + assertTrue(Double.compare(Double.NaN, -Double.MAX_VALUE) == +1); + assertTrue(Double.compare(Double.NaN, Double.NEGATIVE_INFINITY) == +1); + + assertTrue(Double.compare(Double.POSITIVE_INFINITY, Double.NaN) == -1); + assertTrue(Double.compare(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY) == 0); + assertTrue(Double.compare(Double.POSITIVE_INFINITY, Double.MAX_VALUE) == +1); + assertTrue(Double.compare(Double.POSITIVE_INFINITY, 1.2d) == +1); + assertTrue(Double.compare(Double.POSITIVE_INFINITY, 0.0d) == +1); + assertTrue(Double.compare(Double.POSITIVE_INFINITY, -0.0d) == +1); + assertTrue(Double.compare(Double.POSITIVE_INFINITY, -1.2d) == +1); + assertTrue(Double.compare(Double.POSITIVE_INFINITY, -Double.MAX_VALUE) == +1); + assertTrue(Double.compare(Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY) == +1); + + assertTrue(Double.compare(Double.MAX_VALUE, Double.NaN) == -1); + assertTrue(Double.compare(Double.MAX_VALUE, Double.POSITIVE_INFINITY) == -1); + assertTrue(Double.compare(Double.MAX_VALUE, Double.MAX_VALUE) == 0); + assertTrue(Double.compare(Double.MAX_VALUE, 1.2d) == +1); + assertTrue(Double.compare(Double.MAX_VALUE, 0.0d) == +1); + assertTrue(Double.compare(Double.MAX_VALUE, -0.0d) == +1); + assertTrue(Double.compare(Double.MAX_VALUE, -1.2d) == +1); + assertTrue(Double.compare(Double.MAX_VALUE, -Double.MAX_VALUE) == +1); + assertTrue(Double.compare(Double.MAX_VALUE, Double.NEGATIVE_INFINITY) == +1); + + assertTrue(Double.compare(1.2d, Double.NaN) == -1); + assertTrue(Double.compare(1.2d, Double.POSITIVE_INFINITY) == -1); + assertTrue(Double.compare(1.2d, Double.MAX_VALUE) == -1); + assertTrue(Double.compare(1.2d, 1.2d) == 0); + assertTrue(Double.compare(1.2d, 0.0d) == +1); + assertTrue(Double.compare(1.2d, -0.0d) == +1); + assertTrue(Double.compare(1.2d, -1.2d) == +1); + assertTrue(Double.compare(1.2d, -Double.MAX_VALUE) == +1); + assertTrue(Double.compare(1.2d, Double.NEGATIVE_INFINITY) == +1); + + assertTrue(Double.compare(0.0d, Double.NaN) == -1); + assertTrue(Double.compare(0.0d, Double.POSITIVE_INFINITY) == -1); + assertTrue(Double.compare(0.0d, Double.MAX_VALUE) == -1); + assertTrue(Double.compare(0.0d, 1.2d) == -1); + assertTrue(Double.compare(0.0d, 0.0d) == 0); + assertTrue(Double.compare(0.0d, -0.0d) == +1); + assertTrue(Double.compare(0.0d, -1.2d) == +1); + assertTrue(Double.compare(0.0d, -Double.MAX_VALUE) == +1); + assertTrue(Double.compare(0.0d, Double.NEGATIVE_INFINITY) == +1); + + assertTrue(Double.compare(-0.0d, Double.NaN) == -1); + assertTrue(Double.compare(-0.0d, Double.POSITIVE_INFINITY) == -1); + assertTrue(Double.compare(-0.0d, Double.MAX_VALUE) == -1); + assertTrue(Double.compare(-0.0d, 1.2d) == -1); + assertTrue(Double.compare(-0.0d, 0.0d) == -1); + assertTrue(Double.compare(-0.0d, -0.0d) == 0); + assertTrue(Double.compare(-0.0d, -1.2d) == +1); + assertTrue(Double.compare(-0.0d, -Double.MAX_VALUE) == +1); + assertTrue(Double.compare(-0.0d, Double.NEGATIVE_INFINITY) == +1); + + assertTrue(Double.compare(-1.2d, Double.NaN) == -1); + assertTrue(Double.compare(-1.2d, Double.POSITIVE_INFINITY) == -1); + assertTrue(Double.compare(-1.2d, Double.MAX_VALUE) == -1); + assertTrue(Double.compare(-1.2d, 1.2d) == -1); + assertTrue(Double.compare(-1.2d, 0.0d) == -1); + assertTrue(Double.compare(-1.2d, -0.0d) == -1); + assertTrue(Double.compare(-1.2d, -1.2d) == 0); + assertTrue(Double.compare(-1.2d, -Double.MAX_VALUE) == +1); + assertTrue(Double.compare(-1.2d, Double.NEGATIVE_INFINITY) == +1); + + assertTrue(Double.compare(-Double.MAX_VALUE, Double.NaN) == -1); + assertTrue(Double.compare(-Double.MAX_VALUE, Double.POSITIVE_INFINITY) == -1); + assertTrue(Double.compare(-Double.MAX_VALUE, Double.MAX_VALUE) == -1); + assertTrue(Double.compare(-Double.MAX_VALUE, 1.2d) == -1); + assertTrue(Double.compare(-Double.MAX_VALUE, 0.0d) == -1); + assertTrue(Double.compare(-Double.MAX_VALUE, -0.0d) == -1); + assertTrue(Double.compare(-Double.MAX_VALUE, -1.2d) == -1); + assertTrue(Double.compare(-Double.MAX_VALUE, -Double.MAX_VALUE) == 0); + assertTrue(Double.compare(-Double.MAX_VALUE, Double.NEGATIVE_INFINITY) == +1); + + assertTrue(Double.compare(Double.NEGATIVE_INFINITY, Double.NaN) == -1); + assertTrue(Double.compare(Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY) == -1); + assertTrue(Double.compare(Double.NEGATIVE_INFINITY, Double.MAX_VALUE) == -1); + assertTrue(Double.compare(Double.NEGATIVE_INFINITY, 1.2d) == -1); + assertTrue(Double.compare(Double.NEGATIVE_INFINITY, 0.0d) == -1); + assertTrue(Double.compare(Double.NEGATIVE_INFINITY, -0.0d) == -1); + assertTrue(Double.compare(Double.NEGATIVE_INFINITY, -1.2d) == -1); + assertTrue(Double.compare(Double.NEGATIVE_INFINITY, -Double.MAX_VALUE) == -1); + assertTrue(Double.compare(Double.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY) == 0); + } + + @Test + public void testCompareFloat() { + assertTrue(Float.compare(Float.NaN, Float.NaN) == 0); + assertTrue(Float.compare(Float.NaN, Float.POSITIVE_INFINITY) == +1); + assertTrue(Float.compare(Float.NaN, Float.MAX_VALUE) == +1); + assertTrue(Float.compare(Float.NaN, 1.2f) == +1); + assertTrue(Float.compare(Float.NaN, 0.0f) == +1); + assertTrue(Float.compare(Float.NaN, -0.0f) == +1); + assertTrue(Float.compare(Float.NaN, -1.2f) == +1); + assertTrue(Float.compare(Float.NaN, -Float.MAX_VALUE) == +1); + assertTrue(Float.compare(Float.NaN, Float.NEGATIVE_INFINITY) == +1); + + assertTrue(Float.compare(Float.POSITIVE_INFINITY, Float.NaN) == -1); + assertTrue(Float.compare(Float.POSITIVE_INFINITY, Float.POSITIVE_INFINITY) == 0); + assertTrue(Float.compare(Float.POSITIVE_INFINITY, Float.MAX_VALUE) == +1); + assertTrue(Float.compare(Float.POSITIVE_INFINITY, 1.2f) == +1); + assertTrue(Float.compare(Float.POSITIVE_INFINITY, 0.0f) == +1); + assertTrue(Float.compare(Float.POSITIVE_INFINITY, -0.0f) == +1); + assertTrue(Float.compare(Float.POSITIVE_INFINITY, -1.2f) == +1); + assertTrue(Float.compare(Float.POSITIVE_INFINITY, -Float.MAX_VALUE) == +1); + assertTrue(Float.compare(Float.POSITIVE_INFINITY, Float.NEGATIVE_INFINITY) == +1); + + assertTrue(Float.compare(Float.MAX_VALUE, Float.NaN) == -1); + assertTrue(Float.compare(Float.MAX_VALUE, Float.POSITIVE_INFINITY) == -1); + assertTrue(Float.compare(Float.MAX_VALUE, Float.MAX_VALUE) == 0); + assertTrue(Float.compare(Float.MAX_VALUE, 1.2f) == +1); + assertTrue(Float.compare(Float.MAX_VALUE, 0.0f) == +1); + assertTrue(Float.compare(Float.MAX_VALUE, -0.0f) == +1); + assertTrue(Float.compare(Float.MAX_VALUE, -1.2f) == +1); + assertTrue(Float.compare(Float.MAX_VALUE, -Float.MAX_VALUE) == +1); + assertTrue(Float.compare(Float.MAX_VALUE, Float.NEGATIVE_INFINITY) == +1); + + assertTrue(Float.compare(1.2f, Float.NaN) == -1); + assertTrue(Float.compare(1.2f, Float.POSITIVE_INFINITY) == -1); + assertTrue(Float.compare(1.2f, Float.MAX_VALUE) == -1); + assertTrue(Float.compare(1.2f, 1.2f) == 0); + assertTrue(Float.compare(1.2f, 0.0f) == +1); + assertTrue(Float.compare(1.2f, -0.0f) == +1); + assertTrue(Float.compare(1.2f, -1.2f) == +1); + assertTrue(Float.compare(1.2f, -Float.MAX_VALUE) == +1); + assertTrue(Float.compare(1.2f, Float.NEGATIVE_INFINITY) == +1); + + assertTrue(Float.compare(0.0f, Float.NaN) == -1); + assertTrue(Float.compare(0.0f, Float.POSITIVE_INFINITY) == -1); + assertTrue(Float.compare(0.0f, Float.MAX_VALUE) == -1); + assertTrue(Float.compare(0.0f, 1.2f) == -1); + assertTrue(Float.compare(0.0f, 0.0f) == 0); + assertTrue(Float.compare(0.0f, -0.0f) == +1); + assertTrue(Float.compare(0.0f, -1.2f) == +1); + assertTrue(Float.compare(0.0f, -Float.MAX_VALUE) == +1); + assertTrue(Float.compare(0.0f, Float.NEGATIVE_INFINITY) == +1); + + assertTrue(Float.compare(-0.0f, Float.NaN) == -1); + assertTrue(Float.compare(-0.0f, Float.POSITIVE_INFINITY) == -1); + assertTrue(Float.compare(-0.0f, Float.MAX_VALUE) == -1); + assertTrue(Float.compare(-0.0f, 1.2f) == -1); + assertTrue(Float.compare(-0.0f, 0.0f) == -1); + assertTrue(Float.compare(-0.0f, -0.0f) == 0); + assertTrue(Float.compare(-0.0f, -1.2f) == +1); + assertTrue(Float.compare(-0.0f, -Float.MAX_VALUE) == +1); + assertTrue(Float.compare(-0.0f, Float.NEGATIVE_INFINITY) == +1); + + assertTrue(Float.compare(-1.2f, Float.NaN) == -1); + assertTrue(Float.compare(-1.2f, Float.POSITIVE_INFINITY) == -1); + assertTrue(Float.compare(-1.2f, Float.MAX_VALUE) == -1); + assertTrue(Float.compare(-1.2f, 1.2f) == -1); + assertTrue(Float.compare(-1.2f, 0.0f) == -1); + assertTrue(Float.compare(-1.2f, -0.0f) == -1); + assertTrue(Float.compare(-1.2f, -1.2f) == 0); + assertTrue(Float.compare(-1.2f, -Float.MAX_VALUE) == +1); + assertTrue(Float.compare(-1.2f, Float.NEGATIVE_INFINITY) == +1); + + assertTrue(Float.compare(-Float.MAX_VALUE, Float.NaN) == -1); + assertTrue(Float.compare(-Float.MAX_VALUE, Float.POSITIVE_INFINITY) == -1); + assertTrue(Float.compare(-Float.MAX_VALUE, Float.MAX_VALUE) == -1); + assertTrue(Float.compare(-Float.MAX_VALUE, 1.2f) == -1); + assertTrue(Float.compare(-Float.MAX_VALUE, 0.0f) == -1); + assertTrue(Float.compare(-Float.MAX_VALUE, -0.0f) == -1); + assertTrue(Float.compare(-Float.MAX_VALUE, -1.2f) == -1); + assertTrue(Float.compare(-Float.MAX_VALUE, -Float.MAX_VALUE) == 0); + assertTrue(Float.compare(-Float.MAX_VALUE, Float.NEGATIVE_INFINITY) == +1); + + assertTrue(Float.compare(Float.NEGATIVE_INFINITY, Float.NaN) == -1); + assertTrue(Float.compare(Float.NEGATIVE_INFINITY, Float.POSITIVE_INFINITY) == -1); + assertTrue(Float.compare(Float.NEGATIVE_INFINITY, Float.MAX_VALUE) == -1); + assertTrue(Float.compare(Float.NEGATIVE_INFINITY, 1.2f) == -1); + assertTrue(Float.compare(Float.NEGATIVE_INFINITY, 0.0f) == -1); + assertTrue(Float.compare(Float.NEGATIVE_INFINITY, -0.0f) == -1); + assertTrue(Float.compare(Float.NEGATIVE_INFINITY, -1.2f) == -1); + assertTrue(Float.compare(Float.NEGATIVE_INFINITY, -Float.MAX_VALUE) == -1); + assertTrue(Float.compare(Float.NEGATIVE_INFINITY, Float.NEGATIVE_INFINITY) == 0); + } + + @Test + public void testIsDigits() { + assertFalse("isDigits(null) failed", NumberUtils.isDigits(null)); + assertFalse("isDigits('') failed", NumberUtils.isDigits("")); + assertTrue("isDigits(String) failed", NumberUtils.isDigits("12345")); + assertFalse("isDigits(String) neg 1 failed", NumberUtils.isDigits("1234.5")); + assertFalse("isDigits(String) neg 3 failed", NumberUtils.isDigits("1ab")); + assertFalse("isDigits(String) neg 4 failed", NumberUtils.isDigits("abc")); + } + + /** + * Tests isNumber(String) and tests that createNumber(String) returns + * a valid number iff isNumber(String) returns false. + */ + @Test + public void testIsNumber() { + String val = "12345"; + assertTrue("isNumber(String) 1 failed", NumberUtils.isNumber(val)); + assertTrue("isNumber(String)/createNumber(String) 1 failed", checkCreateNumber(val)); + val = "1234.5"; + assertTrue("isNumber(String) 2 failed", NumberUtils.isNumber(val)); + assertTrue("isNumber(String)/createNumber(String) 2 failed", checkCreateNumber(val)); + val = ".12345"; + assertTrue("isNumber(String) 3 failed", NumberUtils.isNumber(val)); + assertTrue("isNumber(String)/createNumber(String) 3 failed", checkCreateNumber(val)); + val = "1234E5"; + assertTrue("isNumber(String) 4 failed", NumberUtils.isNumber(val)); + assertTrue("isNumber(String)/createNumber(String) 4 failed", checkCreateNumber(val)); + val = "1234E+5"; + assertTrue("isNumber(String) 5 failed", NumberUtils.isNumber(val)); + assertTrue("isNumber(String)/createNumber(String) 5 failed", checkCreateNumber(val)); + val = "1234E-5"; + assertTrue("isNumber(String) 6 failed", NumberUtils.isNumber(val)); + assertTrue("isNumber(String)/createNumber(String) 6 failed", checkCreateNumber(val)); + val = "123.4E5"; + assertTrue("isNumber(String) 7 failed", NumberUtils.isNumber(val)); + assertTrue("isNumber(String)/createNumber(String) 7 failed", checkCreateNumber(val)); + val = "-1234"; + assertTrue("isNumber(String) 8 failed", NumberUtils.isNumber(val)); + assertTrue("isNumber(String)/createNumber(String) 8 failed", checkCreateNumber(val)); + val = "-1234.5"; + assertTrue("isNumber(String) 9 failed", NumberUtils.isNumber(val)); + assertTrue("isNumber(String)/createNumber(String) 9 failed", checkCreateNumber(val)); + val = "-.12345"; + assertTrue("isNumber(String) 10 failed", NumberUtils.isNumber(val)); + assertTrue("isNumber(String)/createNumber(String) 10 failed", checkCreateNumber(val)); + val = "-1234E5"; + assertTrue("isNumber(String) 11 failed", NumberUtils.isNumber(val)); + assertTrue("isNumber(String)/createNumber(String) 11 failed", checkCreateNumber(val)); + val = "0"; + assertTrue("isNumber(String) 12 failed", NumberUtils.isNumber(val)); + assertTrue("isNumber(String)/createNumber(String) 12 failed", checkCreateNumber(val)); + val = "-0"; + assertTrue("isNumber(String) 13 failed", NumberUtils.isNumber(val)); + assertTrue("isNumber(String)/createNumber(String) 13 failed", checkCreateNumber(val)); + val = "01234"; + assertTrue("isNumber(String) 14 failed", NumberUtils.isNumber(val)); + assertTrue("isNumber(String)/createNumber(String) 14 failed", checkCreateNumber(val)); + val = "-01234"; + assertTrue("isNumber(String) 15 failed", NumberUtils.isNumber(val)); + assertTrue("isNumber(String)/createNumber(String) 15 failed", checkCreateNumber(val)); + val = "0xABC123"; + assertTrue("isNumber(String) 16 failed", NumberUtils.isNumber(val)); + assertTrue("isNumber(String)/createNumber(String) 16 failed", checkCreateNumber(val)); + val = "0x0"; + assertTrue("isNumber(String) 17 failed", NumberUtils.isNumber(val)); + assertTrue("isNumber(String)/createNumber(String) 17 failed", checkCreateNumber(val)); + val = "123.4E21D"; + assertTrue("isNumber(String) 19 failed", NumberUtils.isNumber(val)); + assertTrue("isNumber(String)/createNumber(String) 19 failed", checkCreateNumber(val)); + val = "-221.23F"; + assertTrue("isNumber(String) 20 failed", NumberUtils.isNumber(val)); + assertTrue("isNumber(String)/createNumber(String) 20 failed", checkCreateNumber(val)); + val = "22338L"; + assertTrue("isNumber(String) 21 failed", NumberUtils.isNumber(val)); + assertTrue("isNumber(String)/createNumber(String) 21 failed", checkCreateNumber(val)); + val = null; + assertTrue("isNumber(String) 1 Neg failed", !NumberUtils.isNumber(val)); + assertTrue("isNumber(String)/createNumber(String) 1 Neg failed", !checkCreateNumber(val)); + val = ""; + assertTrue("isNumber(String) 2 Neg failed", !NumberUtils.isNumber(val)); + assertTrue("isNumber(String)/createNumber(String) 2 Neg failed", !checkCreateNumber(val)); + val = "--2.3"; + assertTrue("isNumber(String) 3 Neg failed", !NumberUtils.isNumber(val)); + assertTrue("isNumber(String)/createNumber(String) 3 Neg failed", !checkCreateNumber(val)); + val = ".12.3"; + assertTrue("isNumber(String) 4 Neg failed", !NumberUtils.isNumber(val)); + assertTrue("isNumber(String)/createNumber(String) 4 Neg failed", !checkCreateNumber(val)); + val = "-123E"; + assertTrue("isNumber(String) 5 Neg failed", !NumberUtils.isNumber(val)); + assertTrue("isNumber(String)/createNumber(String) 5 Neg failed", !checkCreateNumber(val)); + val = "-123E+-212"; + assertTrue("isNumber(String) 6 Neg failed", !NumberUtils.isNumber(val)); + assertTrue("isNumber(String)/createNumber(String) 6 Neg failed", !checkCreateNumber(val)); + val = "-123E2.12"; + assertTrue("isNumber(String) 7 Neg failed", !NumberUtils.isNumber(val)); + assertTrue("isNumber(String)/createNumber(String) 7 Neg failed", !checkCreateNumber(val)); + val = "0xGF"; + assertTrue("isNumber(String) 8 Neg failed", !NumberUtils.isNumber(val)); + assertTrue("isNumber(String)/createNumber(String) 8 Neg failed", !checkCreateNumber(val)); + val = "0xFAE-1"; + assertTrue("isNumber(String) 9 Neg failed", !NumberUtils.isNumber(val)); + assertTrue("isNumber(String)/createNumber(String) 9 Neg failed", !checkCreateNumber(val)); + val = "."; + assertTrue("isNumber(String) 10 Neg failed", !NumberUtils.isNumber(val)); + assertTrue("isNumber(String)/createNumber(String) 10 Neg failed", !checkCreateNumber(val)); + val = "-0ABC123"; + assertTrue("isNumber(String) 11 Neg failed", !NumberUtils.isNumber(val)); + assertTrue("isNumber(String)/createNumber(String) 11 Neg failed", !checkCreateNumber(val)); + val = "123.4E-D"; + assertTrue("isNumber(String) 12 Neg failed", !NumberUtils.isNumber(val)); + assertTrue("isNumber(String)/createNumber(String) 12 Neg failed", !checkCreateNumber(val)); + val = "123.4ED"; + assertTrue("isNumber(String) 13 Neg failed", !NumberUtils.isNumber(val)); + assertTrue("isNumber(String)/createNumber(String) 13 Neg failed", !checkCreateNumber(val)); + val = "1234E5l"; + assertTrue("isNumber(String) 14 Neg failed", !NumberUtils.isNumber(val)); + assertTrue("isNumber(String)/createNumber(String) 14 Neg failed", !checkCreateNumber(val)); + val = "11a"; + assertTrue("isNumber(String) 15 Neg failed", !NumberUtils.isNumber(val)); + assertTrue("isNumber(String)/createNumber(String) 15 Neg failed", !checkCreateNumber(val)); + val = "1a"; + assertTrue("isNumber(String) 16 Neg failed", !NumberUtils.isNumber(val)); + assertTrue("isNumber(String)/createNumber(String) 16 Neg failed", !checkCreateNumber(val)); + val = "a"; + assertTrue("isNumber(String) 17 Neg failed", !NumberUtils.isNumber(val)); + assertTrue("isNumber(String)/createNumber(String) 17 Neg failed", !checkCreateNumber(val)); + val = "11g"; + assertTrue("isNumber(String) 18 Neg failed", !NumberUtils.isNumber(val)); + assertTrue("isNumber(String)/createNumber(String) 18 Neg failed", !checkCreateNumber(val)); + val = "11z"; + assertTrue("isNumber(String) 19 Neg failed", !NumberUtils.isNumber(val)); + assertTrue("isNumber(String)/createNumber(String) 19 Neg failed", !checkCreateNumber(val)); + val = "11def"; + assertTrue("isNumber(String) 20 Neg failed", !NumberUtils.isNumber(val)); + assertTrue("isNumber(String)/createNumber(String) 20 Neg failed", !checkCreateNumber(val)); + val = "11d11"; + assertTrue("isNumber(String) 21 Neg failed", !NumberUtils.isNumber(val)); + assertTrue("isNumber(String)/createNumber(String) 21 Neg failed", !checkCreateNumber(val)); + val = "11 11"; + assertTrue("isNumber(String) 22 Neg failed", !NumberUtils.isNumber(val)); + assertTrue("isNumber(String)/createNumber(String) 22 Neg failed", !checkCreateNumber(val)); + val = " 1111"; + assertTrue("isNumber(String) 23 Neg failed", !NumberUtils.isNumber(val)); + assertTrue("isNumber(String)/createNumber(String) 23 Neg failed", !checkCreateNumber(val)); + val = "1111 "; + assertTrue("isNumber(String) 24 Neg failed", !NumberUtils.isNumber(val)); + assertTrue("isNumber(String)/createNumber(String) 24 Neg failed", !checkCreateNumber(val)); + + // LANG-521 + val = "2."; + assertTrue("isNumber(String) LANG-521 failed", NumberUtils.isNumber(val)); + + // LANG-664 + val = "1.1L"; + assertFalse("isNumber(String) LANG-664 failed", NumberUtils.isNumber(val)); + } + + private boolean checkCreateNumber(String val) { + try { + Object obj = NumberUtils.createNumber(val); + if (obj == null) { + return false; + } + return true; + } catch (NumberFormatException e) { + return false; + } + } + + @SuppressWarnings("cast") // suppress instanceof warning check + @Test + public void testConstants() { + assertTrue(NumberUtils.LONG_ZERO instanceof Long); + assertTrue(NumberUtils.LONG_ONE instanceof Long); + assertTrue(NumberUtils.LONG_MINUS_ONE instanceof Long); + assertTrue(NumberUtils.INTEGER_ZERO instanceof Integer); + assertTrue(NumberUtils.INTEGER_ONE instanceof Integer); + assertTrue(NumberUtils.INTEGER_MINUS_ONE instanceof Integer); + assertTrue(NumberUtils.SHORT_ZERO instanceof Short); + assertTrue(NumberUtils.SHORT_ONE instanceof Short); + assertTrue(NumberUtils.SHORT_MINUS_ONE instanceof Short); + assertTrue(NumberUtils.BYTE_ZERO instanceof Byte); + assertTrue(NumberUtils.BYTE_ONE instanceof Byte); + assertTrue(NumberUtils.BYTE_MINUS_ONE instanceof Byte); + assertTrue(NumberUtils.DOUBLE_ZERO instanceof Double); + assertTrue(NumberUtils.DOUBLE_ONE instanceof Double); + assertTrue(NumberUtils.DOUBLE_MINUS_ONE instanceof Double); + assertTrue(NumberUtils.FLOAT_ZERO instanceof Float); + assertTrue(NumberUtils.FLOAT_ONE instanceof Float); + assertTrue(NumberUtils.FLOAT_MINUS_ONE instanceof Float); + + assertTrue(NumberUtils.LONG_ZERO.longValue() == 0); + assertTrue(NumberUtils.LONG_ONE.longValue() == 1); + assertTrue(NumberUtils.LONG_MINUS_ONE.longValue() == -1); + assertTrue(NumberUtils.INTEGER_ZERO.intValue() == 0); + assertTrue(NumberUtils.INTEGER_ONE.intValue() == 1); + assertTrue(NumberUtils.INTEGER_MINUS_ONE.intValue() == -1); + assertTrue(NumberUtils.SHORT_ZERO.shortValue() == 0); + assertTrue(NumberUtils.SHORT_ONE.shortValue() == 1); + assertTrue(NumberUtils.SHORT_MINUS_ONE.shortValue() == -1); + assertTrue(NumberUtils.BYTE_ZERO.byteValue() == 0); + assertTrue(NumberUtils.BYTE_ONE.byteValue() == 1); + assertTrue(NumberUtils.BYTE_MINUS_ONE.byteValue() == -1); + assertTrue(NumberUtils.DOUBLE_ZERO.doubleValue() == 0.0d); + assertTrue(NumberUtils.DOUBLE_ONE.doubleValue() == 1.0d); + assertTrue(NumberUtils.DOUBLE_MINUS_ONE.doubleValue() == -1.0d); + assertTrue(NumberUtils.FLOAT_ZERO.floatValue() == 0.0f); + assertTrue(NumberUtils.FLOAT_ONE.floatValue() == 1.0f); + assertTrue(NumberUtils.FLOAT_MINUS_ONE.floatValue() == -1.0f); + } + + @Test + public void testLang300() { + NumberUtils.createNumber("-1l"); + NumberUtils.createNumber("01l"); + NumberUtils.createNumber("1l"); + } + + @Test + public void testLang381() { + assertTrue(Double.isNaN(NumberUtils.min(1.2, 2.5, Double.NaN))); + assertTrue(Double.isNaN(NumberUtils.max(1.2, 2.5, Double.NaN))); + assertTrue(Float.isNaN(NumberUtils.min(1.2f, 2.5f, Float.NaN))); + assertTrue(Float.isNaN(NumberUtils.max(1.2f, 2.5f, Float.NaN))); + + double[] a = new double[] { 1.2, Double.NaN, 3.7, 27.0, 42.0, Double.NaN }; + assertTrue(Double.isNaN(NumberUtils.max(a))); + assertTrue(Double.isNaN(NumberUtils.min(a))); + + double[] b = new double[] { Double.NaN, 1.2, Double.NaN, 3.7, 27.0, 42.0, Double.NaN }; + assertTrue(Double.isNaN(NumberUtils.max(b))); + assertTrue(Double.isNaN(NumberUtils.min(b))); + + float[] aF = new float[] { 1.2f, Float.NaN, 3.7f, 27.0f, 42.0f, Float.NaN }; + assertTrue(Float.isNaN(NumberUtils.max(aF))); + + float[] bF = new float[] { Float.NaN, 1.2f, Float.NaN, 3.7f, 27.0f, 42.0f, Float.NaN }; + assertTrue(Float.isNaN(NumberUtils.max(bF))); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/math/package.html b/ApacheCommonsLang/org/apache/commons/lang3/math/package.html new file mode 100644 index 0000000..a70089a --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/math/package.html @@ -0,0 +1,25 @@ + + + +Extends {@link java.math} for business mathematical classes. This package is intended for business +mathematical use, not scientific use. See Commons Math +for a more complete set of mathematical classes. +@since 2.0 +

These classes are immutable, and therefore thread-safe.

+ + diff --git a/ApacheCommonsLang/org/apache/commons/lang3/mutable/Mutable.java b/ApacheCommonsLang/org/apache/commons/lang3/mutable/Mutable.java new file mode 100644 index 0000000..64514f0 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/mutable/Mutable.java @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.lang3.mutable; + +/** + * Provides mutable access to a value. + *

+ * Mutable is used as a generic interface to the implementations in this package. + *

+ * A typical use case would be to enable a primitive or string to be passed to a method and allow that method to + * effectively change the value of the primitive/string. Another use case is to store a frequently changing primitive in + * a collection (for example a total in a map) without needing to create new Integer/Long wrapper objects. + * + * @since 2.1 + * @param the type to set and get + * @version $Id: Mutable.java 1153213 2011-08-02 17:35:39Z ggregory $ + */ +public interface Mutable { + + /** + * Gets the value of this mutable. + * + * @return the stored value + */ + T getValue(); + + /** + * Sets the value of this mutable. + * + * @param value + * the value to store + * @throws NullPointerException + * if the object is null and null is invalid + * @throws ClassCastException + * if the type is invalid + */ + void setValue(T value); + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableBoolean.java b/ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableBoolean.java new file mode 100644 index 0000000..2d12e21 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableBoolean.java @@ -0,0 +1,196 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.lang3.mutable; + +import java.io.Serializable; + +/** + * A mutable boolean wrapper. + *

+ * Note that as MutableBoolean does not extend Boolean, it is not treated by String.format as a Boolean parameter. + * + * @see Boolean + * @since 2.2 + * @version $Id: MutableBoolean.java 1309977 2012-04-05 17:53:39Z ggregory $ + */ +public class MutableBoolean implements Mutable, Serializable, Comparable { + + /** + * Required for serialization support. + * + * @see java.io.Serializable + */ + private static final long serialVersionUID = -4830728138360036487L; + + /** The mutable value. */ + private boolean value; + + /** + * Constructs a new MutableBoolean with the default value of false. + */ + public MutableBoolean() { + super(); + } + + /** + * Constructs a new MutableBoolean with the specified value. + * + * @param value the initial value to store + */ + public MutableBoolean(boolean value) { + super(); + this.value = value; + } + + /** + * Constructs a new MutableBoolean with the specified value. + * + * @param value the initial value to store, not null + * @throws NullPointerException if the object is null + */ + public MutableBoolean(Boolean value) { + super(); + this.value = value.booleanValue(); + } + + //----------------------------------------------------------------------- + /** + * Gets the value as a Boolean instance. + * + * @return the value as a Boolean, never null + */ + @Override + public Boolean getValue() { + return Boolean.valueOf(this.value); + } + + /** + * Sets the value. + * + * @param value the value to set + */ + public void setValue(boolean value) { + this.value = value; + } + + /** + * Sets the value from any Boolean instance. + * + * @param value the value to set, not null + * @throws NullPointerException if the object is null + */ + @Override + public void setValue(Boolean value) { + this.value = value.booleanValue(); + } + + //----------------------------------------------------------------------- + /** + * Checks if the current value is true. + * + * @return true if the current value is true + * @since 2.5 + */ + public boolean isTrue() { + return value == true; + } + + /** + * Checks if the current value is false. + * + * @return true if the current value is false + * @since 2.5 + */ + public boolean isFalse() { + return value == false; + } + + //----------------------------------------------------------------------- + /** + * Returns the value of this MutableBoolean as a boolean. + * + * @return the boolean value represented by this object. + */ + public boolean booleanValue() { + return value; + } + + //----------------------------------------------------------------------- + /** + * Gets this mutable as an instance of Boolean. + * + * @return a Boolean instance containing the value from this mutable, never null + * @since 2.5 + */ + public Boolean toBoolean() { + return Boolean.valueOf(booleanValue()); + } + + //----------------------------------------------------------------------- + /** + * Compares this object to the specified object. The result is true if and only if the argument is + * not null and is an MutableBoolean object that contains the same + * boolean value as this object. + * + * @param obj the object to compare with, null returns false + * @return true if the objects are the same; false otherwise. + */ + @Override + public boolean equals(Object obj) { + if (obj instanceof MutableBoolean) { + return value == ((MutableBoolean) obj).booleanValue(); + } + return false; + } + + /** + * Returns a suitable hash code for this mutable. + * + * @return the hash code returned by Boolean.TRUE or Boolean.FALSE + */ + @Override + public int hashCode() { + return value ? Boolean.TRUE.hashCode() : Boolean.FALSE.hashCode(); + } + + //----------------------------------------------------------------------- + /** + * Compares this mutable to another in ascending order. + * + * @param other the other mutable to compare to, not null + * @return negative if this is less, zero if equal, positive if greater + * where false is less than true + */ + @Override + public int compareTo(MutableBoolean other) { + boolean anotherVal = other.value; + return value == anotherVal ? 0 : (value ? 1 : -1); + } + + //----------------------------------------------------------------------- + /** + * Returns the String value of this mutable. + * + * @return the mutable value as a string + */ + @Override + public String toString() { + return String.valueOf(value); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableBooleanTest.java b/ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableBooleanTest.java new file mode 100644 index 0000000..8daa45d --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableBooleanTest.java @@ -0,0 +1,131 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.lang3.mutable; + +import junit.framework.TestCase; + +/** + * JUnit tests. + * + * @since 2.2 + * @see MutableBoolean + * @version $Id: MutableBooleanTest.java 1088899 2011-04-05 05:31:27Z bayard $ + */ +public class MutableBooleanTest extends TestCase { + + public MutableBooleanTest(String testName) { + super(testName); + } + + public void testCompareTo() { + final MutableBoolean mutBool = new MutableBoolean(false); + + assertEquals(0, mutBool.compareTo(new MutableBoolean(false))); + assertEquals(-1, mutBool.compareTo(new MutableBoolean(true))); + mutBool.setValue(true); + assertEquals(+1, mutBool.compareTo(new MutableBoolean(false))); + assertEquals(0, mutBool.compareTo(new MutableBoolean(true))); + + try { + mutBool.compareTo(null); + fail(); + } catch (NullPointerException ex) { + } + } + + // ---------------------------------------------------------------- + public void testConstructors() { + assertEquals(false, new MutableBoolean().booleanValue()); + + assertEquals(true, new MutableBoolean(true).booleanValue()); + assertEquals(false, new MutableBoolean(false).booleanValue()); + + assertEquals(true, new MutableBoolean(Boolean.TRUE).booleanValue()); + assertEquals(false, new MutableBoolean(Boolean.FALSE).booleanValue()); + + try { + new MutableBoolean(null); + fail(); + } catch (NullPointerException ex) { + } + } + + public void testEquals() { + final MutableBoolean mutBoolA = new MutableBoolean(false); + final MutableBoolean mutBoolB = new MutableBoolean(false); + final MutableBoolean mutBoolC = new MutableBoolean(true); + + assertEquals(true, mutBoolA.equals(mutBoolA)); + assertEquals(true, mutBoolA.equals(mutBoolB)); + assertEquals(true, mutBoolB.equals(mutBoolA)); + assertEquals(true, mutBoolB.equals(mutBoolB)); + assertEquals(false, mutBoolA.equals(mutBoolC)); + assertEquals(false, mutBoolB.equals(mutBoolC)); + assertEquals(true, mutBoolC.equals(mutBoolC)); + assertEquals(false, mutBoolA.equals(null)); + assertEquals(false, mutBoolA.equals(Boolean.FALSE)); + assertEquals(false, mutBoolA.equals("false")); + } + + public void testGetSet() { + assertEquals(false, new MutableBoolean().booleanValue()); + assertEquals(Boolean.FALSE, new MutableBoolean().getValue()); + + final MutableBoolean mutBool = new MutableBoolean(false); + assertEquals(Boolean.FALSE, mutBool.toBoolean()); + assertEquals(false, mutBool.booleanValue()); + assertEquals(true, mutBool.isFalse()); + assertEquals(false, mutBool.isTrue()); + + mutBool.setValue(Boolean.TRUE); + assertEquals(Boolean.TRUE, mutBool.toBoolean()); + assertEquals(true, mutBool.booleanValue()); + assertEquals(false, mutBool.isFalse()); + assertEquals(true, mutBool.isTrue()); + + mutBool.setValue(false); + assertEquals(false, mutBool.booleanValue()); + + mutBool.setValue(true); + assertEquals(true, mutBool.booleanValue()); + + try { + mutBool.setValue(null); + fail(); + } catch (NullPointerException ex) { + } + } + + public void testHashCode() { + final MutableBoolean mutBoolA = new MutableBoolean(false); + final MutableBoolean mutBoolB = new MutableBoolean(false); + final MutableBoolean mutBoolC = new MutableBoolean(true); + + assertEquals(true, mutBoolA.hashCode() == mutBoolA.hashCode()); + assertEquals(true, mutBoolA.hashCode() == mutBoolB.hashCode()); + assertEquals(false, mutBoolA.hashCode() == mutBoolC.hashCode()); + assertEquals(true, mutBoolA.hashCode() == Boolean.FALSE.hashCode()); + assertEquals(true, mutBoolC.hashCode() == Boolean.TRUE.hashCode()); + } + + public void testToString() { + assertEquals(Boolean.FALSE.toString(), new MutableBoolean(false).toString()); + assertEquals(Boolean.TRUE.toString(), new MutableBoolean(true).toString()); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableByte.java b/ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableByte.java new file mode 100644 index 0000000..7543ca3 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableByte.java @@ -0,0 +1,286 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.mutable; + +/** + * A mutable byte wrapper. + *

+ * Note that as MutableByte does not extend Byte, it is not treated by String.format as a Byte parameter. + * + * @see Byte + * @since 2.1 + * @version $Id: MutableByte.java 1309977 2012-04-05 17:53:39Z ggregory $ + */ +public class MutableByte extends Number implements Comparable, Mutable { + + /** + * Required for serialization support. + * + * @see java.io.Serializable + */ + private static final long serialVersionUID = -1585823265L; + + /** The mutable value. */ + private byte value; + + /** + * Constructs a new MutableByte with the default value of zero. + */ + public MutableByte() { + super(); + } + + /** + * Constructs a new MutableByte with the specified value. + * + * @param value the initial value to store + */ + public MutableByte(byte value) { + super(); + this.value = value; + } + + /** + * Constructs a new MutableByte with the specified value. + * + * @param value the initial value to store, not null + * @throws NullPointerException if the object is null + */ + public MutableByte(Number value) { + super(); + this.value = value.byteValue(); + } + + /** + * Constructs a new MutableByte parsing the given string. + * + * @param value the string to parse, not null + * @throws NumberFormatException if the string cannot be parsed into a byte + * @since 2.5 + */ + public MutableByte(String value) throws NumberFormatException { + super(); + this.value = Byte.parseByte(value); + } + + //----------------------------------------------------------------------- + /** + * Gets the value as a Byte instance. + * + * @return the value as a Byte, never null + */ + @Override + public Byte getValue() { + return Byte.valueOf(this.value); + } + + /** + * Sets the value. + * + * @param value the value to set + */ + public void setValue(byte value) { + this.value = value; + } + + /** + * Sets the value from any Number instance. + * + * @param value the value to set, not null + * @throws NullPointerException if the object is null + */ + @Override + public void setValue(Number value) { + this.value = value.byteValue(); + } + + //----------------------------------------------------------------------- + /** + * Increments the value. + * + * @since Commons Lang 2.2 + */ + public void increment() { + value++; + } + + /** + * Decrements the value. + * + * @since Commons Lang 2.2 + */ + public void decrement() { + value--; + } + + //----------------------------------------------------------------------- + /** + * Adds a value to the value of this instance. + * + * @param operand the value to add, not null + * @since Commons Lang 2.2 + */ + public void add(byte operand) { + this.value += operand; + } + + /** + * Adds a value to the value of this instance. + * + * @param operand the value to add, not null + * @throws NullPointerException if the object is null + * @since Commons Lang 2.2 + */ + public void add(Number operand) { + this.value += operand.byteValue(); + } + + /** + * Subtracts a value from the value of this instance. + * + * @param operand the value to subtract, not null + * @since Commons Lang 2.2 + */ + public void subtract(byte operand) { + this.value -= operand; + } + + /** + * Subtracts a value from the value of this instance. + * + * @param operand the value to subtract, not null + * @throws NullPointerException if the object is null + * @since Commons Lang 2.2 + */ + public void subtract(Number operand) { + this.value -= operand.byteValue(); + } + + //----------------------------------------------------------------------- + // shortValue relies on Number implementation + /** + * Returns the value of this MutableByte as a byte. + * + * @return the numeric value represented by this object after conversion to type byte. + */ + @Override + public byte byteValue() { + return value; + } + + /** + * Returns the value of this MutableByte as an int. + * + * @return the numeric value represented by this object after conversion to type int. + */ + @Override + public int intValue() { + return value; + } + + /** + * Returns the value of this MutableByte as a long. + * + * @return the numeric value represented by this object after conversion to type long. + */ + @Override + public long longValue() { + return value; + } + + /** + * Returns the value of this MutableByte as a float. + * + * @return the numeric value represented by this object after conversion to type float. + */ + @Override + public float floatValue() { + return value; + } + + /** + * Returns the value of this MutableByte as a double. + * + * @return the numeric value represented by this object after conversion to type double. + */ + @Override + public double doubleValue() { + return value; + } + + //----------------------------------------------------------------------- + /** + * Gets this mutable as an instance of Byte. + * + * @return a Byte instance containing the value from this mutable + */ + public Byte toByte() { + return Byte.valueOf(byteValue()); + } + + //----------------------------------------------------------------------- + /** + * Compares this object to the specified object. The result is true if and only if the argument is + * not null and is a MutableByte object that contains the same byte value + * as this object. + * + * @param obj the object to compare with, null returns false + * @return true if the objects are the same; false otherwise. + */ + @Override + public boolean equals(Object obj) { + if (obj instanceof MutableByte) { + return value == ((MutableByte) obj).byteValue(); + } + return false; + } + + /** + * Returns a suitable hash code for this mutable. + * + * @return a suitable hash code + */ + @Override + public int hashCode() { + return value; + } + + //----------------------------------------------------------------------- + /** + * Compares this mutable to another in ascending order. + * + * @param other the other mutable to compare to, not null + * @return negative if this is less, zero if equal, positive if greater + */ + @Override + public int compareTo(MutableByte other) { + byte anotherVal = other.value; + return value < anotherVal ? -1 : (value == anotherVal ? 0 : 1); + } + + //----------------------------------------------------------------------- + /** + * Returns the String value of this mutable. + * + * @return the mutable value as a string + */ + @Override + public String toString() { + return String.valueOf(value); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableByteTest.java b/ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableByteTest.java new file mode 100644 index 0000000..347c328 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableByteTest.java @@ -0,0 +1,178 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.mutable; + +import junit.framework.TestCase; + +/** + * JUnit tests. + * + * @version $Id: MutableByteTest.java 1153484 2011-08-03 13:39:42Z ggregory $ + * @see MutableByte + */ +public class MutableByteTest extends TestCase { + + public MutableByteTest(String testName) { + super(testName); + } + + // ---------------------------------------------------------------- + public void testConstructors() { + assertEquals((byte) 0, new MutableByte().byteValue()); + + assertEquals((byte) 1, new MutableByte((byte) 1).byteValue()); + + assertEquals((byte) 2, new MutableByte(Byte.valueOf((byte) 2)).byteValue()); + assertEquals((byte) 3, new MutableByte(new MutableByte((byte) 3)).byteValue()); + + assertEquals((byte) 2, new MutableByte("2").byteValue()); + + try { + new MutableByte((Number)null); + fail(); + } catch (NullPointerException ex) {} + } + + public void testGetSet() { + final MutableByte mutNum = new MutableByte((byte) 0); + assertEquals((byte) 0, new MutableByte().byteValue()); + assertEquals(Byte.valueOf((byte) 0), new MutableByte().getValue()); + + mutNum.setValue((byte) 1); + assertEquals((byte) 1, mutNum.byteValue()); + assertEquals(Byte.valueOf((byte) 1), mutNum.getValue()); + + mutNum.setValue(Byte.valueOf((byte) 2)); + assertEquals((byte) 2, mutNum.byteValue()); + assertEquals(Byte.valueOf((byte) 2), mutNum.getValue()); + + mutNum.setValue(new MutableByte((byte) 3)); + assertEquals((byte) 3, mutNum.byteValue()); + assertEquals(Byte.valueOf((byte) 3), mutNum.getValue()); + try { + mutNum.setValue(null); + fail(); + } catch (NullPointerException ex) {} + } + + public void testEquals() { + final MutableByte mutNumA = new MutableByte((byte) 0); + final MutableByte mutNumB = new MutableByte((byte) 0); + final MutableByte mutNumC = new MutableByte((byte) 1); + + assertEquals(true, mutNumA.equals(mutNumA)); + assertEquals(true, mutNumA.equals(mutNumB)); + assertEquals(true, mutNumB.equals(mutNumA)); + assertEquals(true, mutNumB.equals(mutNumB)); + assertEquals(false, mutNumA.equals(mutNumC)); + assertEquals(false, mutNumB.equals(mutNumC)); + assertEquals(true, mutNumC.equals(mutNumC)); + assertEquals(false, mutNumA.equals(null)); + assertEquals(false, mutNumA.equals(Byte.valueOf((byte) 0))); + assertEquals(false, mutNumA.equals("0")); + } + + public void testHashCode() { + final MutableByte mutNumA = new MutableByte((byte) 0); + final MutableByte mutNumB = new MutableByte((byte) 0); + final MutableByte mutNumC = new MutableByte((byte) 1); + + assertEquals(true, mutNumA.hashCode() == mutNumA.hashCode()); + assertEquals(true, mutNumA.hashCode() == mutNumB.hashCode()); + assertEquals(false, mutNumA.hashCode() == mutNumC.hashCode()); + assertEquals(true, mutNumA.hashCode() == Byte.valueOf((byte) 0).hashCode()); + } + + public void testCompareTo() { + final MutableByte mutNum = new MutableByte((byte) 0); + + assertEquals((byte) 0, mutNum.compareTo(new MutableByte((byte) 0))); + assertEquals((byte) +1, mutNum.compareTo(new MutableByte((byte) -1))); + assertEquals((byte) -1, mutNum.compareTo(new MutableByte((byte) 1))); + try { + mutNum.compareTo(null); + fail(); + } catch (NullPointerException ex) {} + } + + public void testPrimitiveValues() { + MutableByte mutNum = new MutableByte( (byte) 1 ); + + assertEquals( 1.0F, mutNum.floatValue(), 0 ); + assertEquals( 1.0, mutNum.doubleValue(), 0 ); + assertEquals( (byte) 1, mutNum.byteValue() ); + assertEquals( (short) 1, mutNum.shortValue() ); + assertEquals( 1, mutNum.intValue() ); + assertEquals( 1L, mutNum.longValue() ); + } + + public void testToByte() { + assertEquals(Byte.valueOf((byte) 0), new MutableByte((byte) 0).toByte()); + assertEquals(Byte.valueOf((byte) 123), new MutableByte((byte) 123).toByte()); + } + + public void testIncrement() { + MutableByte mutNum = new MutableByte((byte) 1); + mutNum.increment(); + + assertEquals(2, mutNum.intValue()); + assertEquals(2L, mutNum.longValue()); + } + + public void testDecrement() { + MutableByte mutNum = new MutableByte((byte) 1); + mutNum.decrement(); + + assertEquals(0, mutNum.intValue()); + assertEquals(0L, mutNum.longValue()); + } + + public void testAddValuePrimitive() { + MutableByte mutNum = new MutableByte((byte) 1); + mutNum.add((byte)1); + + assertEquals((byte) 2, mutNum.byteValue()); + } + + public void testAddValueObject() { + MutableByte mutNum = new MutableByte((byte) 1); + mutNum.add(Integer.valueOf(1)); + + assertEquals((byte) 2, mutNum.byteValue()); + } + + public void testSubtractValuePrimitive() { + MutableByte mutNum = new MutableByte((byte) 1); + mutNum.subtract((byte) 1); + + assertEquals((byte) 0, mutNum.byteValue()); + } + + public void testSubtractValueObject() { + MutableByte mutNum = new MutableByte((byte) 1); + mutNum.subtract(Integer.valueOf(1)); + + assertEquals((byte) 0, mutNum.byteValue()); + } + + public void testToString() { + assertEquals("0", new MutableByte((byte) 0).toString()); + assertEquals("10", new MutableByte((byte) 10).toString()); + assertEquals("-123", new MutableByte((byte) -123).toString()); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableDouble.java b/ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableDouble.java new file mode 100644 index 0000000..de80696 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableDouble.java @@ -0,0 +1,315 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.mutable; + +/** + * A mutable double wrapper. + *

+ * Note that as MutableDouble does not extend Double, it is not treated by String.format as a Double parameter. + * + * @see Double + * @since 2.1 + * @version $Id: MutableDouble.java 1309977 2012-04-05 17:53:39Z ggregory $ + */ +public class MutableDouble extends Number implements Comparable, Mutable { + + /** + * Required for serialization support. + * + * @see java.io.Serializable + */ + private static final long serialVersionUID = 1587163916L; + + /** The mutable value. */ + private double value; + + /** + * Constructs a new MutableDouble with the default value of zero. + */ + public MutableDouble() { + super(); + } + + /** + * Constructs a new MutableDouble with the specified value. + * + * @param value the initial value to store + */ + public MutableDouble(double value) { + super(); + this.value = value; + } + + /** + * Constructs a new MutableDouble with the specified value. + * + * @param value the initial value to store, not null + * @throws NullPointerException if the object is null + */ + public MutableDouble(Number value) { + super(); + this.value = value.doubleValue(); + } + + /** + * Constructs a new MutableDouble parsing the given string. + * + * @param value the string to parse, not null + * @throws NumberFormatException if the string cannot be parsed into a double + * @since 2.5 + */ + public MutableDouble(String value) throws NumberFormatException { + super(); + this.value = Double.parseDouble(value); + } + + //----------------------------------------------------------------------- + /** + * Gets the value as a Double instance. + * + * @return the value as a Double, never null + */ + @Override + public Double getValue() { + return Double.valueOf(this.value); + } + + /** + * Sets the value. + * + * @param value the value to set + */ + public void setValue(double value) { + this.value = value; + } + + /** + * Sets the value from any Number instance. + * + * @param value the value to set, not null + * @throws NullPointerException if the object is null + */ + @Override + public void setValue(Number value) { + this.value = value.doubleValue(); + } + + //----------------------------------------------------------------------- + /** + * Checks whether the double value is the special NaN value. + * + * @return true if NaN + */ + public boolean isNaN() { + return Double.isNaN(value); + } + + /** + * Checks whether the double value is infinite. + * + * @return true if infinite + */ + public boolean isInfinite() { + return Double.isInfinite(value); + } + + //----------------------------------------------------------------------- + /** + * Increments the value. + * + * @since Commons Lang 2.2 + */ + public void increment() { + value++; + } + + /** + * Decrements the value. + * + * @since Commons Lang 2.2 + */ + public void decrement() { + value--; + } + + //----------------------------------------------------------------------- + /** + * Adds a value to the value of this instance. + * + * @param operand the value to add + * @since Commons Lang 2.2 + */ + public void add(double operand) { + this.value += operand; + } + + /** + * Adds a value to the value of this instance. + * + * @param operand the value to add, not null + * @throws NullPointerException if the object is null + * @since Commons Lang 2.2 + */ + public void add(Number operand) { + this.value += operand.doubleValue(); + } + + /** + * Subtracts a value from the value of this instance. + * + * @param operand the value to subtract, not null + * @since Commons Lang 2.2 + */ + public void subtract(double operand) { + this.value -= operand; + } + + /** + * Subtracts a value from the value of this instance. + * + * @param operand the value to subtract, not null + * @throws NullPointerException if the object is null + * @since Commons Lang 2.2 + */ + public void subtract(Number operand) { + this.value -= operand.doubleValue(); + } + + //----------------------------------------------------------------------- + // shortValue and byteValue rely on Number implementation + /** + * Returns the value of this MutableDouble as an int. + * + * @return the numeric value represented by this object after conversion to type int. + */ + @Override + public int intValue() { + return (int) value; + } + + /** + * Returns the value of this MutableDouble as a long. + * + * @return the numeric value represented by this object after conversion to type long. + */ + @Override + public long longValue() { + return (long) value; + } + + /** + * Returns the value of this MutableDouble as a float. + * + * @return the numeric value represented by this object after conversion to type float. + */ + @Override + public float floatValue() { + return (float) value; + } + + /** + * Returns the value of this MutableDouble as a double. + * + * @return the numeric value represented by this object after conversion to type double. + */ + @Override + public double doubleValue() { + return value; + } + + //----------------------------------------------------------------------- + /** + * Gets this mutable as an instance of Double. + * + * @return a Double instance containing the value from this mutable, never null + */ + public Double toDouble() { + return Double.valueOf(doubleValue()); + } + + //----------------------------------------------------------------------- + /** + * Compares this object against the specified object. The result is true if and only if the argument + * is not null and is a Double object that represents a double that has the identical + * bit pattern to the bit pattern of the double represented by this object. For this purpose, two + * double values are considered to be the same if and only if the method + * {@link Double#doubleToLongBits(double)}returns the same long value when applied to each. + *

+ * Note that in most cases, for two instances of class Double,d1 and d2, + * the value of d1.equals(d2) is true if and only if

+ * + *
+     *   d1.doubleValue() == d2.doubleValue()
+     * 
+ * + *
+ *

+ * also has the value true. However, there are two exceptions: + *

    + *
  • If d1 and d2 both represent Double.NaN, then the + * equals method returns true, even though Double.NaN==Double.NaN has + * the value false. + *
  • If d1 represents +0.0 while d2 represents -0.0, + * or vice versa, the equal test has the value false, even though + * +0.0==-0.0 has the value true. This allows hashtables to operate properly. + *
+ * + * @param obj the object to compare with, null returns false + * @return true if the objects are the same; false otherwise. + */ + @Override + public boolean equals(Object obj) { + return obj instanceof MutableDouble + && Double.doubleToLongBits(((MutableDouble) obj).value) == Double.doubleToLongBits(value); + } + + /** + * Returns a suitable hash code for this mutable. + * + * @return a suitable hash code + */ + @Override + public int hashCode() { + long bits = Double.doubleToLongBits(value); + return (int) (bits ^ bits >>> 32); + } + + //----------------------------------------------------------------------- + /** + * Compares this mutable to another in ascending order. + * + * @param other the other mutable to compare to, not null + * @return negative if this is less, zero if equal, positive if greater + */ + @Override + public int compareTo(MutableDouble other) { + double anotherVal = other.value; + return Double.compare(value, anotherVal); + } + + //----------------------------------------------------------------------- + /** + * Returns the String value of this mutable. + * + * @return the mutable value as a string + */ + @Override + public String toString() { + return String.valueOf(value); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableDoubleTest.java b/ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableDoubleTest.java new file mode 100644 index 0000000..54081fe --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableDoubleTest.java @@ -0,0 +1,189 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.mutable; + +import junit.framework.TestCase; + +/** + * JUnit tests. + * + * @version $Id: MutableDoubleTest.java 1153490 2011-08-03 13:53:35Z ggregory $ + * @see MutableDouble + */ +public class MutableDoubleTest extends TestCase { + + public MutableDoubleTest(String testName) { + super(testName); + } + + // ---------------------------------------------------------------- + public void testConstructors() { + assertEquals(0d, new MutableDouble().doubleValue(), 0.0001d); + + assertEquals(1d, new MutableDouble(1d).doubleValue(), 0.0001d); + + assertEquals(2d, new MutableDouble(Double.valueOf(2d)).doubleValue(), 0.0001d); + assertEquals(3d, new MutableDouble(new MutableDouble(3d)).doubleValue(), 0.0001d); + + assertEquals(2d, new MutableDouble("2.0").doubleValue(), 0.0001d); + + try { + new MutableDouble((Number)null); + fail(); + } catch (NullPointerException ex) {} + } + + public void testGetSet() { + final MutableDouble mutNum = new MutableDouble(0d); + assertEquals(0d, new MutableDouble().doubleValue(), 0.0001d); + assertEquals(Double.valueOf(0), new MutableDouble().getValue()); + + mutNum.setValue(1); + assertEquals(1d, mutNum.doubleValue(), 0.0001d); + assertEquals(Double.valueOf(1d), mutNum.getValue()); + + mutNum.setValue(Double.valueOf(2d)); + assertEquals(2d, mutNum.doubleValue(), 0.0001d); + assertEquals(Double.valueOf(2d), mutNum.getValue()); + + mutNum.setValue(new MutableDouble(3d)); + assertEquals(3d, mutNum.doubleValue(), 0.0001d); + assertEquals(Double.valueOf(3d), mutNum.getValue()); + try { + mutNum.setValue(null); + fail(); + } catch (NullPointerException ex) {} + } + + public void testNanInfinite() { + MutableDouble mutNum = new MutableDouble(Double.NaN); + assertEquals(true, mutNum.isNaN()); + + mutNum = new MutableDouble(Double.POSITIVE_INFINITY); + assertEquals(true, mutNum.isInfinite()); + + mutNum = new MutableDouble(Double.NEGATIVE_INFINITY); + assertEquals(true, mutNum.isInfinite()); + } + + public void testEquals() { + final MutableDouble mutNumA = new MutableDouble(0d); + final MutableDouble mutNumB = new MutableDouble(0d); + final MutableDouble mutNumC = new MutableDouble(1d); + + assertEquals(true, mutNumA.equals(mutNumA)); + assertEquals(true, mutNumA.equals(mutNumB)); + assertEquals(true, mutNumB.equals(mutNumA)); + assertEquals(true, mutNumB.equals(mutNumB)); + assertEquals(false, mutNumA.equals(mutNumC)); + assertEquals(false, mutNumB.equals(mutNumC)); + assertEquals(true, mutNumC.equals(mutNumC)); + assertEquals(false, mutNumA.equals(null)); + assertEquals(false, mutNumA.equals(Double.valueOf(0d))); + assertEquals(false, mutNumA.equals("0")); + } + + public void testHashCode() { + final MutableDouble mutNumA = new MutableDouble(0d); + final MutableDouble mutNumB = new MutableDouble(0d); + final MutableDouble mutNumC = new MutableDouble(1d); + + assertEquals(true, mutNumA.hashCode() == mutNumA.hashCode()); + assertEquals(true, mutNumA.hashCode() == mutNumB.hashCode()); + assertEquals(false, mutNumA.hashCode() == mutNumC.hashCode()); + assertEquals(true, mutNumA.hashCode() == Double.valueOf(0d).hashCode()); + } + + public void testCompareTo() { + final MutableDouble mutNum = new MutableDouble(0d); + + assertEquals(0, mutNum.compareTo(new MutableDouble(0d))); + assertEquals(+1, mutNum.compareTo(new MutableDouble(-1d))); + assertEquals(-1, mutNum.compareTo(new MutableDouble(1d))); + try { + mutNum.compareTo(null); + fail(); + } catch (NullPointerException ex) {} + } + + public void testPrimitiveValues() { + MutableDouble mutNum = new MutableDouble(1.7); + + assertEquals( 1.7F, mutNum.floatValue(), 0 ); + assertEquals( 1.7, mutNum.doubleValue(), 0 ); + assertEquals( (byte) 1, mutNum.byteValue() ); + assertEquals( (short) 1, mutNum.shortValue() ); + assertEquals( 1, mutNum.intValue() ); + assertEquals( 1L, mutNum.longValue() ); + } + + public void testToDouble() { + assertEquals(Double.valueOf(0d), new MutableDouble(0d).toDouble()); + assertEquals(Double.valueOf(12.3d), new MutableDouble(12.3d).toDouble()); + } + + public void testIncrement() { + MutableDouble mutNum = new MutableDouble(1); + mutNum.increment(); + + assertEquals(2, mutNum.intValue()); + assertEquals(2L, mutNum.longValue()); + } + + public void testDecrement() { + MutableDouble mutNum = new MutableDouble(1); + mutNum.decrement(); + + assertEquals(0, mutNum.intValue()); + assertEquals(0L, mutNum.longValue()); + } + + public void testAddValuePrimitive() { + MutableDouble mutNum = new MutableDouble(1); + mutNum.add(1.1d); + + assertEquals(2.1d, mutNum.doubleValue(), 0.01d); + } + + public void testAddValueObject() { + MutableDouble mutNum = new MutableDouble(1); + mutNum.add(Double.valueOf(1.1d)); + + assertEquals(2.1d, mutNum.doubleValue(), 0.01d); + } + + public void testSubtractValuePrimitive() { + MutableDouble mutNum = new MutableDouble(1); + mutNum.subtract(0.9d); + + assertEquals(0.1d, mutNum.doubleValue(), 0.01d); + } + + public void testSubtractValueObject() { + MutableDouble mutNum = new MutableDouble(1); + mutNum.subtract(Double.valueOf(0.9d)); + + assertEquals(0.1d, mutNum.doubleValue(), 0.01d); + } + + public void testToString() { + assertEquals("0.0", new MutableDouble(0d).toString()); + assertEquals("10.0", new MutableDouble(10d).toString()); + assertEquals("-123.0", new MutableDouble(-123d).toString()); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableFloat.java b/ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableFloat.java new file mode 100644 index 0000000..5ebfaad --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableFloat.java @@ -0,0 +1,316 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.mutable; + +/** + * A mutable float wrapper. + *

+ * Note that as MutableFloat does not extend Float, it is not treated by String.format as a Float parameter. + * + * @see Float + * @since 2.1 + * @version $Id: MutableFloat.java 1309977 2012-04-05 17:53:39Z ggregory $ + */ +public class MutableFloat extends Number implements Comparable, Mutable { + + /** + * Required for serialization support. + * + * @see java.io.Serializable + */ + private static final long serialVersionUID = 5787169186L; + + /** The mutable value. */ + private float value; + + /** + * Constructs a new MutableFloat with the default value of zero. + */ + public MutableFloat() { + super(); + } + + /** + * Constructs a new MutableFloat with the specified value. + * + * @param value the initial value to store + */ + public MutableFloat(float value) { + super(); + this.value = value; + } + + /** + * Constructs a new MutableFloat with the specified value. + * + * @param value the initial value to store, not null + * @throws NullPointerException if the object is null + */ + public MutableFloat(Number value) { + super(); + this.value = value.floatValue(); + } + + /** + * Constructs a new MutableFloat parsing the given string. + * + * @param value the string to parse, not null + * @throws NumberFormatException if the string cannot be parsed into a float + * @since 2.5 + */ + public MutableFloat(String value) throws NumberFormatException { + super(); + this.value = Float.parseFloat(value); + } + + //----------------------------------------------------------------------- + /** + * Gets the value as a Float instance. + * + * @return the value as a Float, never null + */ + @Override + public Float getValue() { + return Float.valueOf(this.value); + } + + /** + * Sets the value. + * + * @param value the value to set + */ + public void setValue(float value) { + this.value = value; + } + + /** + * Sets the value from any Number instance. + * + * @param value the value to set, not null + * @throws NullPointerException if the object is null + */ + @Override + public void setValue(Number value) { + this.value = value.floatValue(); + } + + //----------------------------------------------------------------------- + /** + * Checks whether the float value is the special NaN value. + * + * @return true if NaN + */ + public boolean isNaN() { + return Float.isNaN(value); + } + + /** + * Checks whether the float value is infinite. + * + * @return true if infinite + */ + public boolean isInfinite() { + return Float.isInfinite(value); + } + + //----------------------------------------------------------------------- + /** + * Increments the value. + * + * @since Commons Lang 2.2 + */ + public void increment() { + value++; + } + + /** + * Decrements the value. + * + * @since Commons Lang 2.2 + */ + public void decrement() { + value--; + } + + //----------------------------------------------------------------------- + /** + * Adds a value to the value of this instance. + * + * @param operand the value to add, not null + * @since Commons Lang 2.2 + */ + public void add(float operand) { + this.value += operand; + } + + /** + * Adds a value to the value of this instance. + * + * @param operand the value to add, not null + * @throws NullPointerException if the object is null + * @since Commons Lang 2.2 + */ + public void add(Number operand) { + this.value += operand.floatValue(); + } + + /** + * Subtracts a value from the value of this instance. + * + * @param operand the value to subtract + * @since Commons Lang 2.2 + */ + public void subtract(float operand) { + this.value -= operand; + } + + /** + * Subtracts a value from the value of this instance. + * + * @param operand the value to subtract, not null + * @throws NullPointerException if the object is null + * @since Commons Lang 2.2 + */ + public void subtract(Number operand) { + this.value -= operand.floatValue(); + } + + //----------------------------------------------------------------------- + // shortValue and byteValue rely on Number implementation + /** + * Returns the value of this MutableFloat as an int. + * + * @return the numeric value represented by this object after conversion to type int. + */ + @Override + public int intValue() { + return (int) value; + } + + /** + * Returns the value of this MutableFloat as a long. + * + * @return the numeric value represented by this object after conversion to type long. + */ + @Override + public long longValue() { + return (long) value; + } + + /** + * Returns the value of this MutableFloat as a float. + * + * @return the numeric value represented by this object after conversion to type float. + */ + @Override + public float floatValue() { + return value; + } + + /** + * Returns the value of this MutableFloat as a double. + * + * @return the numeric value represented by this object after conversion to type double. + */ + @Override + public double doubleValue() { + return value; + } + + //----------------------------------------------------------------------- + /** + * Gets this mutable as an instance of Float. + * + * @return a Float instance containing the value from this mutable, never null + */ + public Float toFloat() { + return Float.valueOf(floatValue()); + } + + //----------------------------------------------------------------------- + /** + * Compares this object against some other object. The result is true if and only if the argument is + * not null and is a Float object that represents a float that has the + * identical bit pattern to the bit pattern of the float represented by this object. For this + * purpose, two float values are considered to be the same if and only if the method + * {@link Float#floatToIntBits(float)}returns the same int value when applied to each. + *

+ * Note that in most cases, for two instances of class Float,f1 and f2, + * the value of f1.equals(f2) is true if and only if

+ * + *
+     *   f1.floatValue() == f2.floatValue()
+     * 
+ * + *
+ *

+ * also has the value true. However, there are two exceptions: + *

    + *
  • If f1 and f2 both represent Float.NaN, then the + * equals method returns true, even though Float.NaN==Float.NaN has + * the value false. + *
  • If f1 represents +0.0f while f2 represents -0.0f, + * or vice versa, the equal test has the value false, even though + * 0.0f==-0.0f has the value true. + *
+ * This definition allows hashtables to operate properly. + * + * @param obj the object to compare with, null returns false + * @return true if the objects are the same; false otherwise. + * @see java.lang.Float#floatToIntBits(float) + */ + @Override + public boolean equals(Object obj) { + return obj instanceof MutableFloat + && Float.floatToIntBits(((MutableFloat) obj).value) == Float.floatToIntBits(value); + } + + /** + * Returns a suitable hash code for this mutable. + * + * @return a suitable hash code + */ + @Override + public int hashCode() { + return Float.floatToIntBits(value); + } + + //----------------------------------------------------------------------- + /** + * Compares this mutable to another in ascending order. + * + * @param other the other mutable to compare to, not null + * @return negative if this is less, zero if equal, positive if greater + */ + @Override + public int compareTo(MutableFloat other) { + float anotherVal = other.value; + return Float.compare(value, anotherVal); + } + + //----------------------------------------------------------------------- + /** + * Returns the String value of this mutable. + * + * @return the mutable value as a string + */ + @Override + public String toString() { + return String.valueOf(value); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableFloatTest.java b/ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableFloatTest.java new file mode 100644 index 0000000..0fb9ab9 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableFloatTest.java @@ -0,0 +1,189 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.mutable; + +import junit.framework.TestCase; + +/** + * JUnit tests. + * + * @version $Id: MutableFloatTest.java 1153490 2011-08-03 13:53:35Z ggregory $ + * @see MutableFloat + */ +public class MutableFloatTest extends TestCase { + + public MutableFloatTest(String testName) { + super(testName); + } + + // ---------------------------------------------------------------- + public void testConstructors() { + assertEquals(0f, new MutableFloat().floatValue(), 0.0001f); + + assertEquals(1f, new MutableFloat(1f).floatValue(), 0.0001f); + + assertEquals(2f, new MutableFloat(Float.valueOf(2f)).floatValue(), 0.0001f); + assertEquals(3f, new MutableFloat(new MutableFloat(3f)).floatValue(), 0.0001f); + + assertEquals(2f, new MutableFloat("2.0").floatValue(), 0.0001f); + + try { + new MutableFloat((Number)null); + fail(); + } catch (NullPointerException ex) {} + } + + public void testGetSet() { + final MutableFloat mutNum = new MutableFloat(0f); + assertEquals(0f, new MutableFloat().floatValue(), 0.0001f); + assertEquals(Float.valueOf(0), new MutableFloat().getValue()); + + mutNum.setValue(1); + assertEquals(1f, mutNum.floatValue(), 0.0001f); + assertEquals(Float.valueOf(1f), mutNum.getValue()); + + mutNum.setValue(Float.valueOf(2f)); + assertEquals(2f, mutNum.floatValue(), 0.0001f); + assertEquals(Float.valueOf(2f), mutNum.getValue()); + + mutNum.setValue(new MutableFloat(3f)); + assertEquals(3f, mutNum.floatValue(), 0.0001f); + assertEquals(Float.valueOf(3f), mutNum.getValue()); + try { + mutNum.setValue(null); + fail(); + } catch (NullPointerException ex) {} + } + + public void testNanInfinite() { + MutableFloat mutNum = new MutableFloat(Float.NaN); + assertEquals(true, mutNum.isNaN()); + + mutNum = new MutableFloat(Float.POSITIVE_INFINITY); + assertEquals(true, mutNum.isInfinite()); + + mutNum = new MutableFloat(Float.NEGATIVE_INFINITY); + assertEquals(true, mutNum.isInfinite()); + } + + public void testEquals() { + final MutableFloat mutNumA = new MutableFloat(0f); + final MutableFloat mutNumB = new MutableFloat(0f); + final MutableFloat mutNumC = new MutableFloat(1f); + + assertEquals(true, mutNumA.equals(mutNumA)); + assertEquals(true, mutNumA.equals(mutNumB)); + assertEquals(true, mutNumB.equals(mutNumA)); + assertEquals(true, mutNumB.equals(mutNumB)); + assertEquals(false, mutNumA.equals(mutNumC)); + assertEquals(false, mutNumB.equals(mutNumC)); + assertEquals(true, mutNumC.equals(mutNumC)); + assertEquals(false, mutNumA.equals(null)); + assertEquals(false, mutNumA.equals(Float.valueOf(0f))); + assertEquals(false, mutNumA.equals("0")); + } + + public void testHashCode() { + final MutableFloat mutNumA = new MutableFloat(0f); + final MutableFloat mutNumB = new MutableFloat(0f); + final MutableFloat mutNumC = new MutableFloat(1f); + + assertEquals(true, mutNumA.hashCode() == mutNumA.hashCode()); + assertEquals(true, mutNumA.hashCode() == mutNumB.hashCode()); + assertEquals(false, mutNumA.hashCode() == mutNumC.hashCode()); + assertEquals(true, mutNumA.hashCode() == Float.valueOf(0f).hashCode()); + } + + public void testCompareTo() { + final MutableFloat mutNum = new MutableFloat(0f); + + assertEquals(0, mutNum.compareTo(new MutableFloat(0f))); + assertEquals(+1, mutNum.compareTo(new MutableFloat(-1f))); + assertEquals(-1, mutNum.compareTo(new MutableFloat(1f))); + try { + mutNum.compareTo(null); + fail(); + } catch (NullPointerException ex) {} + } + + public void testPrimitiveValues() { + MutableFloat mutNum = new MutableFloat(1.7F); + + assertEquals( 1, mutNum.intValue() ); + assertEquals( 1.7, mutNum.doubleValue(), 0.00001 ); + assertEquals( (byte) 1, mutNum.byteValue() ); + assertEquals( (short) 1, mutNum.shortValue() ); + assertEquals( 1, mutNum.intValue() ); + assertEquals( 1L, mutNum.longValue() ); + } + + public void testToFloat() { + assertEquals(Float.valueOf(0f), new MutableFloat(0f).toFloat()); + assertEquals(Float.valueOf(12.3f), new MutableFloat(12.3f).toFloat()); + } + + public void testIncrement() { + MutableFloat mutNum = new MutableFloat(1); + mutNum.increment(); + + assertEquals(2, mutNum.intValue()); + assertEquals(2L, mutNum.longValue()); + } + + public void testDecrement() { + MutableFloat mutNum = new MutableFloat(1); + mutNum.decrement(); + + assertEquals(0, mutNum.intValue()); + assertEquals(0L, mutNum.longValue()); + } + + public void testAddValuePrimitive() { + MutableFloat mutNum = new MutableFloat(1); + mutNum.add(1.1f); + + assertEquals(2.1f, mutNum.floatValue(), 0.01f); + } + + public void testAddValueObject() { + MutableFloat mutNum = new MutableFloat(1); + mutNum.add(Float.valueOf(1.1f)); + + assertEquals(2.1f, mutNum.floatValue(), 0.01f); + } + + public void testSubtractValuePrimitive() { + MutableFloat mutNum = new MutableFloat(1); + mutNum.subtract(0.9f); + + assertEquals(0.1f, mutNum.floatValue(), 0.01f); + } + + public void testSubtractValueObject() { + MutableFloat mutNum = new MutableFloat(1); + mutNum.subtract(Float.valueOf(0.9f)); + + assertEquals(0.1f, mutNum.floatValue(), 0.01f); + } + + public void testToString() { + assertEquals("0.0", new MutableFloat(0f).toString()); + assertEquals("10.0", new MutableFloat(10f).toString()); + assertEquals("-123.0", new MutableFloat(-123f).toString()); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableInt.java b/ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableInt.java new file mode 100644 index 0000000..c24de00 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableInt.java @@ -0,0 +1,276 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.mutable; + +/** + * A mutable int wrapper. + *

+ * Note that as MutableInt does not extend Integer, it is not treated by String.format as an Integer parameter. + * + * @see Integer + * @since 2.1 + * @version $Id: MutableInt.java 1309977 2012-04-05 17:53:39Z ggregory $ + */ +public class MutableInt extends Number implements Comparable, Mutable { + + /** + * Required for serialization support. + * + * @see java.io.Serializable + */ + private static final long serialVersionUID = 512176391864L; + + /** The mutable value. */ + private int value; + + /** + * Constructs a new MutableInt with the default value of zero. + */ + public MutableInt() { + super(); + } + + /** + * Constructs a new MutableInt with the specified value. + * + * @param value the initial value to store + */ + public MutableInt(int value) { + super(); + this.value = value; + } + + /** + * Constructs a new MutableInt with the specified value. + * + * @param value the initial value to store, not null + * @throws NullPointerException if the object is null + */ + public MutableInt(Number value) { + super(); + this.value = value.intValue(); + } + + /** + * Constructs a new MutableInt parsing the given string. + * + * @param value the string to parse, not null + * @throws NumberFormatException if the string cannot be parsed into an int + * @since 2.5 + */ + public MutableInt(String value) throws NumberFormatException { + super(); + this.value = Integer.parseInt(value); + } + + //----------------------------------------------------------------------- + /** + * Gets the value as a Integer instance. + * + * @return the value as a Integer, never null + */ + @Override + public Integer getValue() { + return Integer.valueOf(this.value); + } + + /** + * Sets the value. + * + * @param value the value to set + */ + public void setValue(int value) { + this.value = value; + } + + /** + * Sets the value from any Number instance. + * + * @param value the value to set, not null + * @throws NullPointerException if the object is null + */ + @Override + public void setValue(Number value) { + this.value = value.intValue(); + } + + //----------------------------------------------------------------------- + /** + * Increments the value. + * + * @since Commons Lang 2.2 + */ + public void increment() { + value++; + } + + /** + * Decrements the value. + * + * @since Commons Lang 2.2 + */ + public void decrement() { + value--; + } + + //----------------------------------------------------------------------- + /** + * Adds a value to the value of this instance. + * + * @param operand the value to add, not null + * @since Commons Lang 2.2 + */ + public void add(int operand) { + this.value += operand; + } + + /** + * Adds a value to the value of this instance. + * + * @param operand the value to add, not null + * @throws NullPointerException if the object is null + * @since Commons Lang 2.2 + */ + public void add(Number operand) { + this.value += operand.intValue(); + } + + /** + * Subtracts a value from the value of this instance. + * + * @param operand the value to subtract, not null + * @since Commons Lang 2.2 + */ + public void subtract(int operand) { + this.value -= operand; + } + + /** + * Subtracts a value from the value of this instance. + * + * @param operand the value to subtract, not null + * @throws NullPointerException if the object is null + * @since Commons Lang 2.2 + */ + public void subtract(Number operand) { + this.value -= operand.intValue(); + } + + //----------------------------------------------------------------------- + // shortValue and byteValue rely on Number implementation + /** + * Returns the value of this MutableInt as an int. + * + * @return the numeric value represented by this object after conversion to type int. + */ + @Override + public int intValue() { + return value; + } + + /** + * Returns the value of this MutableInt as a long. + * + * @return the numeric value represented by this object after conversion to type long. + */ + @Override + public long longValue() { + return value; + } + + /** + * Returns the value of this MutableInt as a float. + * + * @return the numeric value represented by this object after conversion to type float. + */ + @Override + public float floatValue() { + return value; + } + + /** + * Returns the value of this MutableInt as a double. + * + * @return the numeric value represented by this object after conversion to type double. + */ + @Override + public double doubleValue() { + return value; + } + + //----------------------------------------------------------------------- + /** + * Gets this mutable as an instance of Integer. + * + * @return a Integer instance containing the value from this mutable, never null + */ + public Integer toInteger() { + return Integer.valueOf(intValue()); + } + + //----------------------------------------------------------------------- + /** + * Compares this object to the specified object. The result is true if and only if the argument is + * not null and is a MutableInt object that contains the same int value + * as this object. + * + * @param obj the object to compare with, null returns false + * @return true if the objects are the same; false otherwise. + */ + @Override + public boolean equals(Object obj) { + if (obj instanceof MutableInt) { + return value == ((MutableInt) obj).intValue(); + } + return false; + } + + /** + * Returns a suitable hash code for this mutable. + * + * @return a suitable hash code + */ + @Override + public int hashCode() { + return value; + } + + //----------------------------------------------------------------------- + /** + * Compares this mutable to another in ascending order. + * + * @param other the other mutable to compare to, not null + * @return negative if this is less, zero if equal, positive if greater + */ + @Override + public int compareTo(MutableInt other) { + int anotherVal = other.value; + return value < anotherVal ? -1 : (value == anotherVal ? 0 : 1); + } + + //----------------------------------------------------------------------- + /** + * Returns the String value of this mutable. + * + * @return the mutable value as a string + */ + @Override + public String toString() { + return String.valueOf(value); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableIntTest.java b/ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableIntTest.java new file mode 100644 index 0000000..1e7dc08 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableIntTest.java @@ -0,0 +1,188 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.mutable; + +import junit.framework.TestCase; + +/** + * JUnit tests. + * + * @version $Id: MutableIntTest.java 1153484 2011-08-03 13:39:42Z ggregory $ + * @see MutableInt + */ +public class MutableIntTest extends TestCase { + + public MutableIntTest(String testName) { + super(testName); + } + + // ---------------------------------------------------------------- + public void testConstructors() { + assertEquals(0, new MutableInt().intValue()); + + assertEquals(1, new MutableInt(1).intValue()); + + assertEquals(2, new MutableInt(Integer.valueOf(2)).intValue()); + assertEquals(3, new MutableInt(new MutableLong(3)).intValue()); + + assertEquals(2, new MutableInt("2").intValue()); + + try { + new MutableInt((Number)null); + fail(); + } catch (NullPointerException ex) {} + } + + public void testGetSet() { + final MutableInt mutNum = new MutableInt(0); + assertEquals(0, new MutableInt().intValue()); + assertEquals(Integer.valueOf(0), new MutableInt().getValue()); + + mutNum.setValue(1); + assertEquals(1, mutNum.intValue()); + assertEquals(Integer.valueOf(1), mutNum.getValue()); + + mutNum.setValue(Integer.valueOf(2)); + assertEquals(2, mutNum.intValue()); + assertEquals(Integer.valueOf(2), mutNum.getValue()); + + mutNum.setValue(new MutableLong(3)); + assertEquals(3, mutNum.intValue()); + assertEquals(Integer.valueOf(3), mutNum.getValue()); + try { + mutNum.setValue(null); + fail(); + } catch (NullPointerException ex) {} + } + + public void testEquals() { + this.testEquals(new MutableInt(0), new MutableInt(0), new MutableInt(1)); + // Should Numbers be supported? GaryG July-21-2005. + //this.testEquals(mutNumA, Integer.valueOf(0), mutNumC); + } + + /** + * @param numA must not be a 0 Integer; must not equal numC. + * @param numB must equal numA; must not equal numC. + * @param numC must not equal numA; must not equal numC. + */ + void testEquals(final Number numA, final Number numB, final Number numC) { + assertEquals(true, numA.equals(numA)); + assertEquals(true, numA.equals(numB)); + assertEquals(true, numB.equals(numA)); + assertEquals(true, numB.equals(numB)); + assertEquals(false, numA.equals(numC)); + assertEquals(false, numB.equals(numC)); + assertEquals(true, numC.equals(numC)); + assertEquals(false, numA.equals(null)); + assertEquals(false, numA.equals(Integer.valueOf(0))); + assertEquals(false, numA.equals("0")); + } + + public void testHashCode() { + final MutableInt mutNumA = new MutableInt(0); + final MutableInt mutNumB = new MutableInt(0); + final MutableInt mutNumC = new MutableInt(1); + + assertEquals(true, mutNumA.hashCode() == mutNumA.hashCode()); + assertEquals(true, mutNumA.hashCode() == mutNumB.hashCode()); + assertEquals(false, mutNumA.hashCode() == mutNumC.hashCode()); + assertEquals(true, mutNumA.hashCode() == Integer.valueOf(0).hashCode()); + } + + public void testCompareTo() { + final MutableInt mutNum = new MutableInt(0); + + assertEquals(0, mutNum.compareTo(new MutableInt(0))); + assertEquals(+1, mutNum.compareTo(new MutableInt(-1))); + assertEquals(-1, mutNum.compareTo(new MutableInt(1))); + try { + mutNum.compareTo(null); + fail(); + } catch (NullPointerException ex) {} + } + + public void testPrimitiveValues() { + MutableInt mutNum = new MutableInt(1); + + assertEquals( (byte) 1, mutNum.byteValue() ); + assertEquals( (short) 1, mutNum.shortValue() ); + assertEquals( 1.0F, mutNum.floatValue(), 0 ); + assertEquals( 1.0, mutNum.doubleValue(), 0 ); + assertEquals( 1L, mutNum.longValue() ); + } + + public void testToInteger() { + assertEquals(Integer.valueOf(0), new MutableInt(0).toInteger()); + assertEquals(Integer.valueOf(123), new MutableInt(123).toInteger()); + } + + public void testIncrement() { + MutableInt mutNum = new MutableInt(1); + mutNum.increment(); + + assertEquals(2, mutNum.intValue()); + assertEquals(2L, mutNum.longValue()); + } + + public void testDecrement() { + MutableInt mutNum = new MutableInt(1); + mutNum.decrement(); + + assertEquals(0, mutNum.intValue()); + assertEquals(0L, mutNum.longValue()); + } + + public void testAddValuePrimitive() { + MutableInt mutNum = new MutableInt(1); + mutNum.add(1); + + assertEquals(2, mutNum.intValue()); + assertEquals(2L, mutNum.longValue()); + } + + public void testAddValueObject() { + MutableInt mutNum = new MutableInt(1); + mutNum.add(Integer.valueOf(1)); + + assertEquals(2, mutNum.intValue()); + assertEquals(2L, mutNum.longValue()); + } + + public void testSubtractValuePrimitive() { + MutableInt mutNum = new MutableInt(1); + mutNum.subtract(1); + + assertEquals(0, mutNum.intValue()); + assertEquals(0L, mutNum.longValue()); + } + + public void testSubtractValueObject() { + MutableInt mutNum = new MutableInt(1); + mutNum.subtract(Integer.valueOf(1)); + + assertEquals(0, mutNum.intValue()); + assertEquals(0L, mutNum.longValue()); + } + + public void testToString() { + assertEquals("0", new MutableInt(0).toString()); + assertEquals("10", new MutableInt(10).toString()); + assertEquals("-123", new MutableInt(-123).toString()); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableLong.java b/ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableLong.java new file mode 100644 index 0000000..8ed5ad9 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableLong.java @@ -0,0 +1,276 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.mutable; + +/** + * A mutable long wrapper. + *

+ * Note that as MutableLong does not extend Long, it is not treated by String.format as a Long parameter. + * + * @see Long + * @since 2.1 + * @version $Id: MutableLong.java 1309977 2012-04-05 17:53:39Z ggregory $ + */ +public class MutableLong extends Number implements Comparable, Mutable { + + /** + * Required for serialization support. + * + * @see java.io.Serializable + */ + private static final long serialVersionUID = 62986528375L; + + /** The mutable value. */ + private long value; + + /** + * Constructs a new MutableLong with the default value of zero. + */ + public MutableLong() { + super(); + } + + /** + * Constructs a new MutableLong with the specified value. + * + * @param value the initial value to store + */ + public MutableLong(long value) { + super(); + this.value = value; + } + + /** + * Constructs a new MutableLong with the specified value. + * + * @param value the initial value to store, not null + * @throws NullPointerException if the object is null + */ + public MutableLong(Number value) { + super(); + this.value = value.longValue(); + } + + /** + * Constructs a new MutableLong parsing the given string. + * + * @param value the string to parse, not null + * @throws NumberFormatException if the string cannot be parsed into a long + * @since 2.5 + */ + public MutableLong(String value) throws NumberFormatException { + super(); + this.value = Long.parseLong(value); + } + + //----------------------------------------------------------------------- + /** + * Gets the value as a Long instance. + * + * @return the value as a Long, never null + */ + @Override + public Long getValue() { + return Long.valueOf(this.value); + } + + /** + * Sets the value. + * + * @param value the value to set + */ + public void setValue(long value) { + this.value = value; + } + + /** + * Sets the value from any Number instance. + * + * @param value the value to set, not null + * @throws NullPointerException if the object is null + */ + @Override + public void setValue(Number value) { + this.value = value.longValue(); + } + + //----------------------------------------------------------------------- + /** + * Increments the value. + * + * @since Commons Lang 2.2 + */ + public void increment() { + value++; + } + + /** + * Decrements the value. + * + * @since Commons Lang 2.2 + */ + public void decrement() { + value--; + } + + //----------------------------------------------------------------------- + /** + * Adds a value to the value of this instance. + * + * @param operand the value to add, not null + * @since Commons Lang 2.2 + */ + public void add(long operand) { + this.value += operand; + } + + /** + * Adds a value to the value of this instance. + * + * @param operand the value to add, not null + * @throws NullPointerException if the object is null + * @since Commons Lang 2.2 + */ + public void add(Number operand) { + this.value += operand.longValue(); + } + + /** + * Subtracts a value from the value of this instance. + * + * @param operand the value to subtract, not null + * @since Commons Lang 2.2 + */ + public void subtract(long operand) { + this.value -= operand; + } + + /** + * Subtracts a value from the value of this instance. + * + * @param operand the value to subtract, not null + * @throws NullPointerException if the object is null + * @since Commons Lang 2.2 + */ + public void subtract(Number operand) { + this.value -= operand.longValue(); + } + + //----------------------------------------------------------------------- + // shortValue and byteValue rely on Number implementation + /** + * Returns the value of this MutableLong as an int. + * + * @return the numeric value represented by this object after conversion to type int. + */ + @Override + public int intValue() { + return (int) value; + } + + /** + * Returns the value of this MutableLong as a long. + * + * @return the numeric value represented by this object after conversion to type long. + */ + @Override + public long longValue() { + return value; + } + + /** + * Returns the value of this MutableLong as a float. + * + * @return the numeric value represented by this object after conversion to type float. + */ + @Override + public float floatValue() { + return value; + } + + /** + * Returns the value of this MutableLong as a double. + * + * @return the numeric value represented by this object after conversion to type double. + */ + @Override + public double doubleValue() { + return value; + } + + //----------------------------------------------------------------------- + /** + * Gets this mutable as an instance of Long. + * + * @return a Long instance containing the value from this mutable, never null + */ + public Long toLong() { + return Long.valueOf(longValue()); + } + + //----------------------------------------------------------------------- + /** + * Compares this object to the specified object. The result is true if and only if the argument + * is not null and is a MutableLong object that contains the same long + * value as this object. + * + * @param obj the object to compare with, null returns false + * @return true if the objects are the same; false otherwise. + */ + @Override + public boolean equals(Object obj) { + if (obj instanceof MutableLong) { + return value == ((MutableLong) obj).longValue(); + } + return false; + } + + /** + * Returns a suitable hash code for this mutable. + * + * @return a suitable hash code + */ + @Override + public int hashCode() { + return (int) (value ^ (value >>> 32)); + } + + //----------------------------------------------------------------------- + /** + * Compares this mutable to another in ascending order. + * + * @param other the other mutable to compare to, not null + * @return negative if this is less, zero if equal, positive if greater + */ + @Override + public int compareTo(MutableLong other) { + long anotherVal = other.value; + return value < anotherVal ? -1 : (value == anotherVal ? 0 : 1); + } + + //----------------------------------------------------------------------- + /** + * Returns the String value of this mutable. + * + * @return the mutable value as a string + */ + @Override + public String toString() { + return String.valueOf(value); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableLongTest.java b/ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableLongTest.java new file mode 100644 index 0000000..2d71361 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableLongTest.java @@ -0,0 +1,182 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.mutable; + +import junit.framework.TestCase; + +/** + * JUnit tests. + * + * @version $Id: MutableLongTest.java 1153487 2011-08-03 13:44:51Z ggregory $ + * @see MutableLong + */ +public class MutableLongTest extends TestCase { + + public MutableLongTest(String testName) { + super(testName); + } + + // ---------------------------------------------------------------- + public void testConstructors() { + assertEquals(0, new MutableLong().longValue()); + + assertEquals(1, new MutableLong(1).longValue()); + + assertEquals(2, new MutableLong(Long.valueOf(2)).longValue()); + assertEquals(3, new MutableLong(new MutableLong(3)).longValue()); + + assertEquals(2, new MutableLong("2").longValue()); + + try { + new MutableLong((Number)null); + fail(); + } catch (NullPointerException ex) {} + } + + public void testGetSet() { + final MutableLong mutNum = new MutableLong(0); + assertEquals(0, new MutableLong().longValue()); + assertEquals(Long.valueOf(0), new MutableLong().getValue()); + + mutNum.setValue(1); + assertEquals(1, mutNum.longValue()); + assertEquals(Long.valueOf(1), mutNum.getValue()); + + mutNum.setValue(Long.valueOf(2)); + assertEquals(2, mutNum.longValue()); + assertEquals(Long.valueOf(2), mutNum.getValue()); + + mutNum.setValue(new MutableLong(3)); + assertEquals(3, mutNum.longValue()); + assertEquals(Long.valueOf(3), mutNum.getValue()); + try { + mutNum.setValue(null); + fail(); + } catch (NullPointerException ex) {} + } + + public void testEquals() { + final MutableLong mutNumA = new MutableLong(0); + final MutableLong mutNumB = new MutableLong(0); + final MutableLong mutNumC = new MutableLong(1); + + assertEquals(true, mutNumA.equals(mutNumA)); + assertEquals(true, mutNumA.equals(mutNumB)); + assertEquals(true, mutNumB.equals(mutNumA)); + assertEquals(true, mutNumB.equals(mutNumB)); + assertEquals(false, mutNumA.equals(mutNumC)); + assertEquals(false, mutNumB.equals(mutNumC)); + assertEquals(true, mutNumC.equals(mutNumC)); + assertEquals(false, mutNumA.equals(null)); + assertEquals(false, mutNumA.equals(Long.valueOf(0))); + assertEquals(false, mutNumA.equals("0")); + } + + public void testHashCode() { + final MutableLong mutNumA = new MutableLong(0); + final MutableLong mutNumB = new MutableLong(0); + final MutableLong mutNumC = new MutableLong(1); + + assertEquals(true, mutNumA.hashCode() == mutNumA.hashCode()); + assertEquals(true, mutNumA.hashCode() == mutNumB.hashCode()); + assertEquals(false, mutNumA.hashCode() == mutNumC.hashCode()); + assertEquals(true, mutNumA.hashCode() == Long.valueOf(0).hashCode()); + } + + public void testCompareTo() { + final MutableLong mutNum = new MutableLong(0); + + assertEquals(0, mutNum.compareTo(new MutableLong(0))); + assertEquals(+1, mutNum.compareTo(new MutableLong(-1))); + assertEquals(-1, mutNum.compareTo(new MutableLong(1))); + try { + mutNum.compareTo(null); + fail(); + } catch (NullPointerException ex) {} + } + + public void testPrimitiveValues() { + MutableLong mutNum = new MutableLong(1L); + + assertEquals( 1.0F, mutNum.floatValue(), 0 ); + assertEquals( 1.0, mutNum.doubleValue(), 0 ); + assertEquals( (byte) 1, mutNum.byteValue() ); + assertEquals( (short) 1, mutNum.shortValue() ); + assertEquals( 1, mutNum.intValue() ); + assertEquals( 1L, mutNum.longValue() ); + } + + public void testToLong() { + assertEquals(Long.valueOf(0L), new MutableLong(0L).toLong()); + assertEquals(Long.valueOf(123L), new MutableLong(123L).toLong()); + } + + public void testIncrement() { + MutableLong mutNum = new MutableLong(1); + mutNum.increment(); + + assertEquals(2, mutNum.intValue()); + assertEquals(2L, mutNum.longValue()); + } + + public void testDecrement() { + MutableLong mutNum = new MutableLong(1); + mutNum.decrement(); + + assertEquals(0, mutNum.intValue()); + assertEquals(0L, mutNum.longValue()); + } + + public void testAddValuePrimitive() { + MutableLong mutNum = new MutableLong(1); + mutNum.add(1); + + assertEquals(2, mutNum.intValue()); + assertEquals(2L, mutNum.longValue()); + } + + public void testAddValueObject() { + MutableLong mutNum = new MutableLong(1); + mutNum.add(Long.valueOf(1)); + + assertEquals(2, mutNum.intValue()); + assertEquals(2L, mutNum.longValue()); + } + + public void testSubtractValuePrimitive() { + MutableLong mutNum = new MutableLong(1); + mutNum.subtract(1); + + assertEquals(0, mutNum.intValue()); + assertEquals(0L, mutNum.longValue()); + } + + public void testSubtractValueObject() { + MutableLong mutNum = new MutableLong(1); + mutNum.subtract(Long.valueOf(1)); + + assertEquals(0, mutNum.intValue()); + assertEquals(0L, mutNum.longValue()); + } + + public void testToString() { + assertEquals("0", new MutableLong(0).toString()); + assertEquals("10", new MutableLong(10).toString()); + assertEquals("-123", new MutableLong(-123).toString()); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableObject.java b/ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableObject.java new file mode 100644 index 0000000..7db0dac --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableObject.java @@ -0,0 +1,128 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.lang3.mutable; + +import java.io.Serializable; + +/** + * A mutable Object wrapper. + * + * @since 2.1 + * @version $Id: MutableObject.java 1309977 2012-04-05 17:53:39Z ggregory $ + */ +public class MutableObject implements Mutable, Serializable { + + /** + * Required for serialization support. + * + * @see java.io.Serializable + */ + private static final long serialVersionUID = 86241875189L; + + /** The mutable value. */ + private T value; + + /** + * Constructs a new MutableObject with the default value of null. + */ + public MutableObject() { + super(); + } + + /** + * Constructs a new MutableObject with the specified value. + * + * @param value the initial value to store + */ + public MutableObject(T value) { + super(); + this.value = value; + } + + //----------------------------------------------------------------------- + /** + * Gets the value. + * + * @return the value, may be null + */ + @Override + public T getValue() { + return this.value; + } + + /** + * Sets the value. + * + * @param value the value to set + */ + @Override + public void setValue(T value) { + this.value = value; + } + + //----------------------------------------------------------------------- + /** + *

+ * Compares this object against the specified object. The result is true if and only if the argument + * is not null and is a MutableObject object that contains the same T + * value as this object. + *

+ * + * @param obj the object to compare with, null returns false + * @return true if the objects are the same; + * true if the objects have equivalent value fields; + * false otherwise. + */ + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (this == obj) { + return true; + } + if (this.getClass() == obj.getClass()) { + MutableObject that = (MutableObject) obj; + return this.value.equals(that.value); + } else { + return false; + } + } + + /** + * Returns the value's hash code or 0 if the value is null. + * + * @return the value's hash code or 0 if the value is null. + */ + @Override + public int hashCode() { + return value == null ? 0 : value.hashCode(); + } + + //----------------------------------------------------------------------- + /** + * Returns the String value of this mutable. + * + * @return the mutable value as a string + */ + @Override + public String toString() { + return value == null ? "null" : value.toString(); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableObjectTest.java b/ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableObjectTest.java new file mode 100644 index 0000000..21357d3 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableObjectTest.java @@ -0,0 +1,95 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.mutable; + +import junit.framework.TestCase; + +/** + * JUnit tests. + * + * @version $Id: MutableObjectTest.java 1153490 2011-08-03 13:53:35Z ggregory $ + * @see MutableShort + */ +public class MutableObjectTest extends TestCase { + + public MutableObjectTest(String testName) { + super(testName); + } + + // ---------------------------------------------------------------- + public void testConstructors() { + assertEquals(null, new MutableObject().getValue()); + + Integer i = Integer.valueOf(6); + assertSame(i, new MutableObject(i).getValue()); + assertSame("HI", new MutableObject("HI").getValue()); + assertSame(null, new MutableObject(null).getValue()); + } + + public void testGetSet() { + final MutableObject mutNum = new MutableObject(); + assertEquals(null, new MutableObject().getValue()); + + mutNum.setValue("HELLO"); + assertSame("HELLO", mutNum.getValue()); + + mutNum.setValue(null); + assertSame(null, mutNum.getValue()); + } + + public void testEquals() { + final MutableObject mutNumA = new MutableObject("ALPHA"); + final MutableObject mutNumB = new MutableObject("ALPHA"); + final MutableObject mutNumC = new MutableObject("BETA"); + final MutableObject mutNumD = new MutableObject(null); + + assertEquals(true, mutNumA.equals(mutNumA)); + assertEquals(true, mutNumA.equals(mutNumB)); + assertEquals(true, mutNumB.equals(mutNumA)); + assertEquals(true, mutNumB.equals(mutNumB)); + assertEquals(false, mutNumA.equals(mutNumC)); + assertEquals(false, mutNumB.equals(mutNumC)); + assertEquals(true, mutNumC.equals(mutNumC)); + assertEquals(false, mutNumA.equals(mutNumD)); + assertEquals(true, mutNumD.equals(mutNumD)); + + assertEquals(false, mutNumA.equals(null)); + assertEquals(false, mutNumA.equals(new Object())); + assertEquals(false, mutNumA.equals("0")); + } + + public void testHashCode() { + final MutableObject mutNumA = new MutableObject("ALPHA"); + final MutableObject mutNumB = new MutableObject("ALPHA"); + final MutableObject mutNumC = new MutableObject("BETA"); + final MutableObject mutNumD = new MutableObject(null); + + assertEquals(true, mutNumA.hashCode() == mutNumA.hashCode()); + assertEquals(true, mutNumA.hashCode() == mutNumB.hashCode()); + assertEquals(false, mutNumA.hashCode() == mutNumC.hashCode()); + assertEquals(false, mutNumA.hashCode() == mutNumD.hashCode()); + assertEquals(true, mutNumA.hashCode() == "ALPHA".hashCode()); + assertEquals(0, mutNumD.hashCode()); + } + + public void testToString() { + assertEquals("HI", new MutableObject("HI").toString()); + assertEquals("10.0", new MutableObject(Double.valueOf(10)).toString()); + assertEquals("null", new MutableObject(null).toString()); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableShort.java b/ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableShort.java new file mode 100644 index 0000000..545485b --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableShort.java @@ -0,0 +1,286 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.mutable; + +/** + * A mutable short wrapper. + *

+ * Note that as MutableShort does not extend Short, it is not treated by String.format as a Short parameter. + * + * @see Short + * @since 2.1 + * @version $Id: MutableShort.java 1309977 2012-04-05 17:53:39Z ggregory $ + */ +public class MutableShort extends Number implements Comparable, Mutable { + + /** + * Required for serialization support. + * + * @see java.io.Serializable + */ + private static final long serialVersionUID = -2135791679L; + + /** The mutable value. */ + private short value; + + /** + * Constructs a new MutableShort with the default value of zero. + */ + public MutableShort() { + super(); + } + + /** + * Constructs a new MutableShort with the specified value. + * + * @param value the initial value to store + */ + public MutableShort(short value) { + super(); + this.value = value; + } + + /** + * Constructs a new MutableShort with the specified value. + * + * @param value the initial value to store, not null + * @throws NullPointerException if the object is null + */ + public MutableShort(Number value) { + super(); + this.value = value.shortValue(); + } + + /** + * Constructs a new MutableShort parsing the given string. + * + * @param value the string to parse, not null + * @throws NumberFormatException if the string cannot be parsed into a short + * @since 2.5 + */ + public MutableShort(String value) throws NumberFormatException { + super(); + this.value = Short.parseShort(value); + } + + //----------------------------------------------------------------------- + /** + * Gets the value as a Short instance. + * + * @return the value as a Short, never null + */ + @Override + public Short getValue() { + return Short.valueOf(this.value); + } + + /** + * Sets the value. + * + * @param value the value to set + */ + public void setValue(short value) { + this.value = value; + } + + /** + * Sets the value from any Number instance. + * + * @param value the value to set, not null + * @throws NullPointerException if the object is null + */ + @Override + public void setValue(Number value) { + this.value = value.shortValue(); + } + + //----------------------------------------------------------------------- + /** + * Increments the value. + * + * @since Commons Lang 2.2 + */ + public void increment() { + value++; + } + + /** + * Decrements the value. + * + * @since Commons Lang 2.2 + */ + public void decrement() { + value--; + } + + //----------------------------------------------------------------------- + /** + * Adds a value to the value of this instance. + * + * @param operand the value to add, not null + * @since Commons Lang 2.2 + */ + public void add(short operand) { + this.value += operand; + } + + /** + * Adds a value to the value of this instance. + * + * @param operand the value to add, not null + * @throws NullPointerException if the object is null + * @since Commons Lang 2.2 + */ + public void add(Number operand) { + this.value += operand.shortValue(); + } + + /** + * Subtracts a value from the value of this instance. + * + * @param operand the value to subtract, not null + * @since Commons Lang 2.2 + */ + public void subtract(short operand) { + this.value -= operand; + } + + /** + * Subtracts a value from the value of this instance. + * + * @param operand the value to subtract, not null + * @throws NullPointerException if the object is null + * @since Commons Lang 2.2 + */ + public void subtract(Number operand) { + this.value -= operand.shortValue(); + } + + //----------------------------------------------------------------------- + // byteValue relies on Number implementation + /** + * Returns the value of this MutableShort as a short. + * + * @return the numeric value represented by this object after conversion to type short. + */ + @Override + public short shortValue() { + return value; + } + + /** + * Returns the value of this MutableShort as an int. + * + * @return the numeric value represented by this object after conversion to type int. + */ + @Override + public int intValue() { + return value; + } + + /** + * Returns the value of this MutableShort as a long. + * + * @return the numeric value represented by this object after conversion to type long. + */ + @Override + public long longValue() { + return value; + } + + /** + * Returns the value of this MutableShort as a float. + * + * @return the numeric value represented by this object after conversion to type float. + */ + @Override + public float floatValue() { + return value; + } + + /** + * Returns the value of this MutableShort as a double. + * + * @return the numeric value represented by this object after conversion to type double. + */ + @Override + public double doubleValue() { + return value; + } + + //----------------------------------------------------------------------- + /** + * Gets this mutable as an instance of Short. + * + * @return a Short instance containing the value from this mutable, never null + */ + public Short toShort() { + return Short.valueOf(shortValue()); + } + + //----------------------------------------------------------------------- + /** + * Compares this object to the specified object. The result is true if and only if the argument + * is not null and is a MutableShort object that contains the same short + * value as this object. + * + * @param obj the object to compare with, null returns false + * @return true if the objects are the same; false otherwise. + */ + @Override + public boolean equals(Object obj) { + if (obj instanceof MutableShort) { + return value == ((MutableShort) obj).shortValue(); + } + return false; + } + + /** + * Returns a suitable hash code for this mutable. + * + * @return a suitable hash code + */ + @Override + public int hashCode() { + return value; + } + + //----------------------------------------------------------------------- + /** + * Compares this mutable to another in ascending order. + * + * @param other the other mutable to compare to, not null + * @return negative if this is less, zero if equal, positive if greater + */ + @Override + public int compareTo(MutableShort other) { + short anotherVal = other.value; + return value < anotherVal ? -1 : (value == anotherVal ? 0 : 1); + } + + //----------------------------------------------------------------------- + /** + * Returns the String value of this mutable. + * + * @return the mutable value as a string + */ + @Override + public String toString() { + return String.valueOf(value); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableShortTest.java b/ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableShortTest.java new file mode 100644 index 0000000..4276e4e --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/mutable/MutableShortTest.java @@ -0,0 +1,178 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.mutable; + +import junit.framework.TestCase; + +/** + * JUnit tests. + * + * @version $Id: MutableShortTest.java 1153488 2011-08-03 13:47:49Z ggregory $ + * @see MutableShort + */ +public class MutableShortTest extends TestCase { + + public MutableShortTest(String testName) { + super(testName); + } + + // ---------------------------------------------------------------- + public void testConstructors() { + assertEquals((short) 0, new MutableShort().shortValue()); + + assertEquals((short) 1, new MutableShort((short) 1).shortValue()); + + assertEquals((short) 2, new MutableShort(Short.valueOf((short) 2)).shortValue()); + assertEquals((short) 3, new MutableShort(new MutableShort((short) 3)).shortValue()); + + assertEquals((short) 2, new MutableShort("2").shortValue()); + + try { + new MutableShort((Number)null); + fail(); + } catch (NullPointerException ex) {} + } + + public void testGetSet() { + final MutableShort mutNum = new MutableShort((short) 0); + assertEquals((short) 0, new MutableShort().shortValue()); + assertEquals(Short.valueOf((short) 0), new MutableShort().getValue()); + + mutNum.setValue((short) 1); + assertEquals((short) 1, mutNum.shortValue()); + assertEquals(Short.valueOf((short) 1), mutNum.getValue()); + + mutNum.setValue(Short.valueOf((short) 2)); + assertEquals((short) 2, mutNum.shortValue()); + assertEquals(Short.valueOf((short) 2), mutNum.getValue()); + + mutNum.setValue(new MutableShort((short) 3)); + assertEquals((short) 3, mutNum.shortValue()); + assertEquals(Short.valueOf((short) 3), mutNum.getValue()); + try { + mutNum.setValue(null); + fail(); + } catch (NullPointerException ex) {} + } + + public void testEquals() { + final MutableShort mutNumA = new MutableShort((short) 0); + final MutableShort mutNumB = new MutableShort((short) 0); + final MutableShort mutNumC = new MutableShort((short) 1); + + assertEquals(true, mutNumA.equals(mutNumA)); + assertEquals(true, mutNumA.equals(mutNumB)); + assertEquals(true, mutNumB.equals(mutNumA)); + assertEquals(true, mutNumB.equals(mutNumB)); + assertEquals(false, mutNumA.equals(mutNumC)); + assertEquals(false, mutNumB.equals(mutNumC)); + assertEquals(true, mutNumC.equals(mutNumC)); + assertEquals(false, mutNumA.equals(null)); + assertEquals(false, mutNumA.equals(Short.valueOf((short) 0))); + assertEquals(false, mutNumA.equals("0")); + } + + public void testHashCode() { + final MutableShort mutNumA = new MutableShort((short) 0); + final MutableShort mutNumB = new MutableShort((short) 0); + final MutableShort mutNumC = new MutableShort((short) 1); + + assertEquals(true, mutNumA.hashCode() == mutNumA.hashCode()); + assertEquals(true, mutNumA.hashCode() == mutNumB.hashCode()); + assertEquals(false, mutNumA.hashCode() == mutNumC.hashCode()); + assertEquals(true, mutNumA.hashCode() == Short.valueOf((short) 0).hashCode()); + } + + public void testCompareTo() { + final MutableShort mutNum = new MutableShort((short) 0); + + assertEquals((short) 0, mutNum.compareTo(new MutableShort((short) 0))); + assertEquals((short) +1, mutNum.compareTo(new MutableShort((short) -1))); + assertEquals((short) -1, mutNum.compareTo(new MutableShort((short) 1))); + try { + mutNum.compareTo(null); + fail(); + } catch (NullPointerException ex) {} + } + + public void testPrimitiveValues() { + MutableShort mutNum = new MutableShort( (short) 1 ); + + assertEquals( 1.0F, mutNum.floatValue(), 0 ); + assertEquals( 1.0, mutNum.doubleValue(), 0 ); + assertEquals( (byte) 1, mutNum.byteValue() ); + assertEquals( (short) 1, mutNum.shortValue() ); + assertEquals( 1, mutNum.intValue() ); + assertEquals( 1L, mutNum.longValue() ); + } + + public void testToShort() { + assertEquals(Short.valueOf((short) 0), new MutableShort((short) 0).toShort()); + assertEquals(Short.valueOf((short) 123), new MutableShort((short) 123).toShort()); + } + + public void testIncrement() { + MutableShort mutNum = new MutableShort((short) 1); + mutNum.increment(); + + assertEquals(2, mutNum.intValue()); + assertEquals(2L, mutNum.longValue()); + } + + public void testDecrement() { + MutableShort mutNum = new MutableShort((short) 1); + mutNum.decrement(); + + assertEquals(0, mutNum.intValue()); + assertEquals(0L, mutNum.longValue()); + } + + public void testAddValuePrimitive() { + MutableShort mutNum = new MutableShort((short) 1); + mutNum.add((short) 1); + + assertEquals((short) 2, mutNum.shortValue()); + } + + public void testAddValueObject() { + MutableShort mutNum = new MutableShort((short) 1); + mutNum.add(Short.valueOf((short) 1)); + + assertEquals((short) 2, mutNum.shortValue()); + } + + public void testSubtractValuePrimitive() { + MutableShort mutNum = new MutableShort((short) 1); + mutNum.subtract((short) 1); + + assertEquals((short) 0, mutNum.shortValue()); + } + + public void testSubtractValueObject() { + MutableShort mutNum = new MutableShort((short) 1); + mutNum.subtract(Short.valueOf((short) 1)); + + assertEquals((short) 0, mutNum.shortValue()); + } + + public void testToString() { + assertEquals("0", new MutableShort((short) 0).toString()); + assertEquals("10", new MutableShort((short) 10).toString()); + assertEquals("-123", new MutableShort((short) -123).toString()); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/mutable/package.html b/ApacheCommonsLang/org/apache/commons/lang3/mutable/package.html new file mode 100644 index 0000000..46758bb --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/mutable/package.html @@ -0,0 +1,29 @@ + + + + + + + +Provides typed mutable wrappers to primitive values and Object. +@since 2.1 +

These classes are not thread-safe.

+ + diff --git a/ApacheCommonsLang/org/apache/commons/lang3/overview.html b/ApacheCommonsLang/org/apache/commons/lang3/overview.html new file mode 100644 index 0000000..a384c66 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/overview.html @@ -0,0 +1,23 @@ + + + +

+This document is the API specification for the Apache Commons Lang library. +

+ + diff --git a/ApacheCommonsLang/org/apache/commons/lang3/package.html b/ApacheCommonsLang/org/apache/commons/lang3/package.html new file mode 100644 index 0000000..ba04df3 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/package.html @@ -0,0 +1,25 @@ + + + +Provides highly reusable static utility methods, chiefly concerned +with adding value to the {@link java.lang} classes. +@since 1.0 +

Most of these classes are immutable and thus thread-safe. +However Charset is not currently guaranteed thread-safe under all circumstances.

+ + diff --git a/ApacheCommonsLang/org/apache/commons/lang3/reflect/ConstructorUtils.java b/ApacheCommonsLang/org/apache/commons/lang3/reflect/ConstructorUtils.java new file mode 100644 index 0000000..0f3edcb --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/reflect/ConstructorUtils.java @@ -0,0 +1,289 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.reflect; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Modifier; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.ClassUtils; + +/** + *

Utility reflection methods focused on constructors, modeled after + * {@link MethodUtils}.

+ * + *

Known Limitations

Accessing Public Constructors In A Default + * Access Superclass

There is an issue when invoking public constructors + * contained in a default access superclass. Reflection locates these + * constructors fine and correctly assigns them as public. However, an + * IllegalAccessException is thrown if the constructors is + * invoked.

+ * + *

ConstructorUtils contains a workaround for this situation. It + * will attempt to call setAccessible on this constructor. If this + * call succeeds, then the method can be invoked as normal. This call will only + * succeed when the application has sufficient security privileges. If this call + * fails then a warning will be logged and the method may fail.

+ * + * @since 2.5 + * @version $Id: ConstructorUtils.java 1144010 2011-07-07 20:02:10Z joehni $ + */ +public class ConstructorUtils { + + /** + *

ConstructorUtils instances should NOT be constructed in standard + * programming. Instead, the class should be used as + * ConstructorUtils.invokeConstructor(cls, args).

+ * + *

This constructor is public to permit tools that require a JavaBean + * instance to operate.

+ */ + public ConstructorUtils() { + super(); + } + + /** + *

Returns a new instance of the specified class inferring the right constructor + * from the types of the arguments.

+ * + *

This locates and calls a constructor. + * The constructor signature must match the argument types by assignment compatibility.

+ * + * @param the type to be constructed + * @param cls the class to be constructed, not null + * @param args the array of arguments, null treated as empty + * @return new instance of cls, not null + * + * @throws NoSuchMethodException if a matching constructor cannot be found + * @throws IllegalAccessException if invocation is not permitted by security + * @throws InvocationTargetException if an error occurs on invocation + * @throws InstantiationException if an error occurs on instantiation + * @see #invokeConstructor(java.lang.Class, java.lang.Object[], java.lang.Class[]) + */ + public static T invokeConstructor(Class cls, Object... args) + throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, + InstantiationException { + if (args == null) { + args = ArrayUtils.EMPTY_OBJECT_ARRAY; + } + Class parameterTypes[] = new Class[args.length]; + for (int i = 0; i < args.length; i++) { + parameterTypes[i] = args[i].getClass(); + } + return invokeConstructor(cls, args, parameterTypes); + } + + /** + *

Returns a new instance of the specified class choosing the right constructor + * from the list of parameter types.

+ * + *

This locates and calls a constructor. + * The constructor signature must match the parameter types by assignment compatibility.

+ * + * @param the type to be constructed + * @param cls the class to be constructed, not null + * @param args the array of arguments, null treated as empty + * @param parameterTypes the array of parameter types, null treated as empty + * @return new instance of cls, not null + * + * @throws NoSuchMethodException if a matching constructor cannot be found + * @throws IllegalAccessException if invocation is not permitted by security + * @throws InvocationTargetException if an error occurs on invocation + * @throws InstantiationException if an error occurs on instantiation + * @see Constructor#newInstance + */ + public static T invokeConstructor(Class cls, Object[] args, Class[] parameterTypes) + throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, + InstantiationException { + if (parameterTypes == null) { + parameterTypes = ArrayUtils.EMPTY_CLASS_ARRAY; + } + if (args == null) { + args = ArrayUtils.EMPTY_OBJECT_ARRAY; + } + Constructor ctor = getMatchingAccessibleConstructor(cls, parameterTypes); + if (ctor == null) { + throw new NoSuchMethodException( + "No such accessible constructor on object: " + cls.getName()); + } + return ctor.newInstance(args); + } + + /** + *

Returns a new instance of the specified class inferring the right constructor + * from the types of the arguments.

+ * + *

This locates and calls a constructor. + * The constructor signature must match the argument types exactly.

+ * + * @param the type to be constructed + * @param cls the class to be constructed, not null + * @param args the array of arguments, null treated as empty + * @return new instance of cls, not null + * + * @throws NoSuchMethodException if a matching constructor cannot be found + * @throws IllegalAccessException if invocation is not permitted by security + * @throws InvocationTargetException if an error occurs on invocation + * @throws InstantiationException if an error occurs on instantiation + * @see #invokeExactConstructor(java.lang.Class, java.lang.Object[], java.lang.Class[]) + */ + public static T invokeExactConstructor(Class cls, Object... args) + throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, + InstantiationException { + if (args == null) { + args = ArrayUtils.EMPTY_OBJECT_ARRAY; + } + int arguments = args.length; + Class parameterTypes[] = new Class[arguments]; + for (int i = 0; i < arguments; i++) { + parameterTypes[i] = args[i].getClass(); + } + return invokeExactConstructor(cls, args, parameterTypes); + } + + /** + *

Returns a new instance of the specified class choosing the right constructor + * from the list of parameter types.

+ * + *

This locates and calls a constructor. + * The constructor signature must match the parameter types exactly.

+ * + * @param the type to be constructed + * @param cls the class to be constructed, not null + * @param args the array of arguments, null treated as empty + * @param parameterTypes the array of parameter types, null treated as empty + * @return new instance of cls, not null + * + * @throws NoSuchMethodException if a matching constructor cannot be found + * @throws IllegalAccessException if invocation is not permitted by security + * @throws InvocationTargetException if an error occurs on invocation + * @throws InstantiationException if an error occurs on instantiation + * @see Constructor#newInstance + */ + public static T invokeExactConstructor(Class cls, Object[] args, + Class[] parameterTypes) throws NoSuchMethodException, IllegalAccessException, + InvocationTargetException, InstantiationException { + if (args == null) { + args = ArrayUtils.EMPTY_OBJECT_ARRAY; + } + if (parameterTypes == null) { + parameterTypes = ArrayUtils.EMPTY_CLASS_ARRAY; + } + Constructor ctor = getAccessibleConstructor(cls, parameterTypes); + if (ctor == null) { + throw new NoSuchMethodException( + "No such accessible constructor on object: "+ cls.getName()); + } + return ctor.newInstance(args); + } + + //----------------------------------------------------------------------- + /** + *

Finds a constructor given a class and signature, checking accessibility.

+ * + *

This finds the constructor and ensures that it is accessible. + * The constructor signature must match the parameter types exactly.

+ * + * @param the constructor type + * @param cls the class to find a constructor for, not null + * @param parameterTypes the array of parameter types, null treated as empty + * @return the constructor, null if no matching accessible constructor found + * @see Class#getConstructor + * @see #getAccessibleConstructor(java.lang.reflect.Constructor) + */ + public static Constructor getAccessibleConstructor(Class cls, + Class... parameterTypes) { + try { + return getAccessibleConstructor(cls.getConstructor(parameterTypes)); + } catch (NoSuchMethodException e) { + return null; + } + } + + /** + *

Checks if the specified constructor is accessible.

+ * + *

This simply ensures that the constructor is accessible.

+ * + * @param the constructor type + * @param ctor the prototype constructor object, not null + * @return the constructor, null if no matching accessible constructor found + * @see java.lang.SecurityManager + */ + public static Constructor getAccessibleConstructor(Constructor ctor) { + return MemberUtils.isAccessible(ctor) + && Modifier.isPublic(ctor.getDeclaringClass().getModifiers()) ? ctor : null; + } + + /** + *

Finds an accessible constructor with compatible parameters.

+ * + *

This checks all the constructor and finds one with compatible parameters + * This requires that every parameter is assignable from the given parameter types. + * This is a more flexible search than the normal exact matching algorithm.

+ * + *

First it checks if there is a constructor matching the exact signature. + * If not then all the constructors of the class are checked to see if their + * signatures are assignment compatible with the parameter types. + * The first assignment compatible matching constructor is returned.

+ * + * @param the constructor type + * @param cls the class to find a constructor for, not null + * @param parameterTypes find method with compatible parameters + * @return the constructor, null if no matching accessible constructor found + */ + public static Constructor getMatchingAccessibleConstructor(Class cls, + Class... parameterTypes) { + // see if we can find the constructor directly + // most of the time this works and it's much faster + try { + Constructor ctor = cls.getConstructor(parameterTypes); + MemberUtils.setAccessibleWorkaround(ctor); + return ctor; + } catch (NoSuchMethodException e) { // NOPMD - Swallow + } + Constructor result = null; + /* + * (1) Class.getConstructors() is documented to return Constructor so as + * long as the array is not subsequently modified, everything's fine. + */ + Constructor[] ctors = cls.getConstructors(); + + // return best match: + for (Constructor ctor : ctors) { + // compare parameters + if (ClassUtils.isAssignable(parameterTypes, ctor.getParameterTypes(), true)) { + // get accessible version of constructor + ctor = getAccessibleConstructor(ctor); + if (ctor != null) { + MemberUtils.setAccessibleWorkaround(ctor); + if (result == null + || MemberUtils.compareParameterTypes(ctor.getParameterTypes(), result + .getParameterTypes(), parameterTypes) < 0) { + // temporary variable for annotation, see comment above (1) + @SuppressWarnings("unchecked") + Constructor constructor = (Constructor)ctor; + result = constructor; + } + } + } + } + return result; + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/reflect/ConstructorUtilsTest.java b/ApacheCommonsLang/org/apache/commons/lang3/reflect/ConstructorUtilsTest.java new file mode 100644 index 0000000..0b1c155 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/reflect/ConstructorUtilsTest.java @@ -0,0 +1,224 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.reflect; + +import java.lang.reflect.Constructor; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +import junit.framework.TestCase; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.math.NumberUtils; + +/** + * Unit tests ConstructorUtils + * @version $Id: ConstructorUtilsTest.java 1097487 2011-04-28 14:49:45Z ggregory $ + */ +public class ConstructorUtilsTest extends TestCase { + public static class TestBean { + private String toString; + + public TestBean() { + toString = "()"; + } + + public TestBean(int i) { + toString = "(int)"; + } + + public TestBean(Integer i) { + toString = "(Integer)"; + } + + public TestBean(double d) { + toString = "(double)"; + } + + public TestBean(String s) { + toString = "(String)"; + } + + public TestBean(Object o) { + toString = "(Object)"; + } + + @Override + public String toString() { + return toString; + } + } + + private static class PrivateClass { + @SuppressWarnings("unused") + public PrivateClass() { + } + } + + private Map, Class[]> classCache; + + public ConstructorUtilsTest(String name) { + super(name); + classCache = new HashMap, Class[]>(); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + classCache.clear(); + } + + public void testConstructor() throws Exception { + assertNotNull(MethodUtils.class.newInstance()); + } + + public void testInvokeConstructor() throws Exception { + assertEquals("()", ConstructorUtils.invokeConstructor(TestBean.class, + (Object[]) ArrayUtils.EMPTY_CLASS_ARRAY).toString()); + assertEquals("()", ConstructorUtils.invokeConstructor(TestBean.class, + (Object[]) null).toString()); + assertEquals("()", ConstructorUtils.invokeConstructor(TestBean.class).toString()); + assertEquals("(String)", ConstructorUtils.invokeConstructor( + TestBean.class, "").toString()); + assertEquals("(Object)", ConstructorUtils.invokeConstructor( + TestBean.class, new Object()).toString()); + assertEquals("(Object)", ConstructorUtils.invokeConstructor( + TestBean.class, Boolean.TRUE).toString()); + assertEquals("(Integer)", ConstructorUtils.invokeConstructor( + TestBean.class, NumberUtils.INTEGER_ONE).toString()); + assertEquals("(int)", ConstructorUtils.invokeConstructor( + TestBean.class, NumberUtils.BYTE_ONE).toString()); + assertEquals("(double)", ConstructorUtils.invokeConstructor( + TestBean.class, NumberUtils.LONG_ONE).toString()); + assertEquals("(double)", ConstructorUtils.invokeConstructor( + TestBean.class, NumberUtils.DOUBLE_ONE).toString()); + } + + public void testInvokeExactConstructor() throws Exception { + assertEquals("()", ConstructorUtils.invokeExactConstructor( + TestBean.class, (Object[]) ArrayUtils.EMPTY_CLASS_ARRAY).toString()); + assertEquals("()", ConstructorUtils.invokeExactConstructor( + TestBean.class, (Object[]) null).toString()); + assertEquals("(String)", ConstructorUtils.invokeExactConstructor( + TestBean.class, "").toString()); + assertEquals("(Object)", ConstructorUtils.invokeExactConstructor( + TestBean.class, new Object()).toString()); + assertEquals("(Integer)", ConstructorUtils.invokeExactConstructor( + TestBean.class, NumberUtils.INTEGER_ONE).toString()); + assertEquals("(double)", ConstructorUtils.invokeExactConstructor( + TestBean.class, new Object[] { NumberUtils.DOUBLE_ONE }, + new Class[] { Double.TYPE }).toString()); + + try { + ConstructorUtils.invokeExactConstructor(TestBean.class, + NumberUtils.BYTE_ONE); + fail("should throw NoSuchMethodException"); + } catch (NoSuchMethodException e) { + } + try { + ConstructorUtils.invokeExactConstructor(TestBean.class, + NumberUtils.LONG_ONE); + fail("should throw NoSuchMethodException"); + } catch (NoSuchMethodException e) { + } + try { + ConstructorUtils.invokeExactConstructor(TestBean.class, + Boolean.TRUE); + fail("should throw NoSuchMethodException"); + } catch (NoSuchMethodException e) { + } + } + + public void testGetAccessibleConstructor() throws Exception { + assertNotNull(ConstructorUtils.getAccessibleConstructor(Object.class + .getConstructor(ArrayUtils.EMPTY_CLASS_ARRAY))); + assertNull(ConstructorUtils.getAccessibleConstructor(PrivateClass.class + .getConstructor(ArrayUtils.EMPTY_CLASS_ARRAY))); + } + + public void testGetAccessibleConstructorFromDescription() throws Exception { + assertNotNull(ConstructorUtils.getAccessibleConstructor(Object.class, + ArrayUtils.EMPTY_CLASS_ARRAY)); + assertNull(ConstructorUtils.getAccessibleConstructor( + PrivateClass.class, ArrayUtils.EMPTY_CLASS_ARRAY)); + } + + public void testGetMatchingAccessibleMethod() throws Exception { + expectMatchingAccessibleConstructorParameterTypes(TestBean.class, + ArrayUtils.EMPTY_CLASS_ARRAY, ArrayUtils.EMPTY_CLASS_ARRAY); + expectMatchingAccessibleConstructorParameterTypes(TestBean.class, null, + ArrayUtils.EMPTY_CLASS_ARRAY); + expectMatchingAccessibleConstructorParameterTypes(TestBean.class, + singletonArray(String.class), singletonArray(String.class)); + expectMatchingAccessibleConstructorParameterTypes(TestBean.class, + singletonArray(Object.class), singletonArray(Object.class)); + expectMatchingAccessibleConstructorParameterTypes(TestBean.class, + singletonArray(Boolean.class), singletonArray(Object.class)); + expectMatchingAccessibleConstructorParameterTypes(TestBean.class, + singletonArray(Byte.class), singletonArray(Integer.TYPE)); + expectMatchingAccessibleConstructorParameterTypes(TestBean.class, + singletonArray(Byte.TYPE), singletonArray(Integer.TYPE)); + expectMatchingAccessibleConstructorParameterTypes(TestBean.class, + singletonArray(Short.class), singletonArray(Integer.TYPE)); + expectMatchingAccessibleConstructorParameterTypes(TestBean.class, + singletonArray(Short.TYPE), singletonArray(Integer.TYPE)); + expectMatchingAccessibleConstructorParameterTypes(TestBean.class, + singletonArray(Character.class), singletonArray(Integer.TYPE)); + expectMatchingAccessibleConstructorParameterTypes(TestBean.class, + singletonArray(Character.TYPE), singletonArray(Integer.TYPE)); + expectMatchingAccessibleConstructorParameterTypes(TestBean.class, + singletonArray(Integer.class), singletonArray(Integer.class)); + expectMatchingAccessibleConstructorParameterTypes(TestBean.class, + singletonArray(Integer.TYPE), singletonArray(Integer.TYPE)); + expectMatchingAccessibleConstructorParameterTypes(TestBean.class, + singletonArray(Long.class), singletonArray(Double.TYPE)); + expectMatchingAccessibleConstructorParameterTypes(TestBean.class, + singletonArray(Long.TYPE), singletonArray(Double.TYPE)); + expectMatchingAccessibleConstructorParameterTypes(TestBean.class, + singletonArray(Float.class), singletonArray(Double.TYPE)); + expectMatchingAccessibleConstructorParameterTypes(TestBean.class, + singletonArray(Float.TYPE), singletonArray(Double.TYPE)); + expectMatchingAccessibleConstructorParameterTypes(TestBean.class, + singletonArray(Double.class), singletonArray(Double.TYPE)); + expectMatchingAccessibleConstructorParameterTypes(TestBean.class, + singletonArray(Double.TYPE), singletonArray(Double.TYPE)); + } + + private void expectMatchingAccessibleConstructorParameterTypes(Class cls, + Class[] requestTypes, Class[] actualTypes) { + Constructor c = ConstructorUtils.getMatchingAccessibleConstructor(cls, + requestTypes); + assertTrue(toString(c.getParameterTypes()) + " not equals " + + toString(actualTypes), Arrays.equals(actualTypes, c + .getParameterTypes())); + } + + private String toString(Class[] c) { + return Arrays.asList(c).toString(); + } + + private Class[] singletonArray(Class c) { + Class[] result = classCache.get(c); + if (result == null) { + result = new Class[] { c }; + classCache.put(c, result); + } + return result; + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/reflect/FieldUtils.java b/ApacheCommonsLang/org/apache/commons/lang3/reflect/FieldUtils.java new file mode 100644 index 0000000..62b2fa6 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/reflect/FieldUtils.java @@ -0,0 +1,595 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.reflect; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; + +import org.apache.commons.lang3.ClassUtils; + +/** + * Utilities for working with fields by reflection. Adapted and refactored + * from the dormant [reflect] Commons sandbox component. + *

+ * The ability is provided to break the scoping restrictions coded by the + * programmer. This can allow fields to be changed that shouldn't be. This + * facility should be used with care. + * + * @since 2.5 + * @version $Id: FieldUtils.java 1144929 2011-07-10 18:26:16Z ggregory $ + */ +public class FieldUtils { + + /** + * FieldUtils instances should NOT be constructed in standard programming. + *

+ * This constructor is public to permit tools that require a JavaBean instance + * to operate. + */ + public FieldUtils() { + super(); + } + + /** + * Gets an accessible Field by name respecting scope. + * Superclasses/interfaces will be considered. + * + * @param cls the class to reflect, must not be null + * @param fieldName the field name to obtain + * @return the Field object + * @throws IllegalArgumentException if the class or field name is null + */ + public static Field getField(Class cls, String fieldName) { + Field field = getField(cls, fieldName, false); + MemberUtils.setAccessibleWorkaround(field); + return field; + } + + /** + * Gets an accessible Field by name breaking scope + * if requested. Superclasses/interfaces will be considered. + * + * @param cls the class to reflect, must not be null + * @param fieldName the field name to obtain + * @param forceAccess whether to break scope restrictions using the + * setAccessible method. False will only + * match public fields. + * @return the Field object + * @throws IllegalArgumentException if the class or field name is null + */ + public static Field getField(final Class cls, String fieldName, boolean forceAccess) { + if (cls == null) { + throw new IllegalArgumentException("The class must not be null"); + } + if (fieldName == null) { + throw new IllegalArgumentException("The field name must not be null"); + } + // Sun Java 1.3 has a bugged implementation of getField hence we write the + // code ourselves + + // getField() will return the Field object with the declaring class + // set correctly to the class that declares the field. Thus requesting the + // field on a subclass will return the field from the superclass. + // + // priority order for lookup: + // searchclass private/protected/package/public + // superclass protected/package/public + // private/different package blocks access to further superclasses + // implementedinterface public + + // check up the superclass hierarchy + for (Class acls = cls; acls != null; acls = acls.getSuperclass()) { + try { + Field field = acls.getDeclaredField(fieldName); + // getDeclaredField checks for non-public scopes as well + // and it returns accurate results + if (!Modifier.isPublic(field.getModifiers())) { + if (forceAccess) { + field.setAccessible(true); + } else { + continue; + } + } + return field; + } catch (NoSuchFieldException ex) { // NOPMD + // ignore + } + } + // check the public interface case. This must be manually searched for + // incase there is a public supersuperclass field hidden by a private/package + // superclass field. + Field match = null; + for (Class class1 : ClassUtils.getAllInterfaces(cls)) { + try { + Field test = ((Class) class1).getField(fieldName); + if (match != null) { + throw new IllegalArgumentException("Reference to field " + fieldName + " is ambiguous relative to " + cls + + "; a matching field exists on two or more implemented interfaces."); + } + match = test; + } catch (NoSuchFieldException ex) { // NOPMD + // ignore + } + } + return match; + } + + /** + * Gets an accessible Field by name respecting scope. + * Only the specified class will be considered. + * + * @param cls the class to reflect, must not be null + * @param fieldName the field name to obtain + * @return the Field object + * @throws IllegalArgumentException if the class or field name is null + */ + public static Field getDeclaredField(Class cls, String fieldName) { + return getDeclaredField(cls, fieldName, false); + } + + /** + * Gets an accessible Field by name breaking scope + * if requested. Only the specified class will be considered. + * + * @param cls the class to reflect, must not be null + * @param fieldName the field name to obtain + * @param forceAccess whether to break scope restrictions using the + * setAccessible method. False will only match public fields. + * @return the Field object + * @throws IllegalArgumentException if the class or field name is null + */ + public static Field getDeclaredField(Class cls, String fieldName, boolean forceAccess) { + if (cls == null) { + throw new IllegalArgumentException("The class must not be null"); + } + if (fieldName == null) { + throw new IllegalArgumentException("The field name must not be null"); + } + try { + // only consider the specified class by using getDeclaredField() + Field field = cls.getDeclaredField(fieldName); + if (!MemberUtils.isAccessible(field)) { + if (forceAccess) { + field.setAccessible(true); + } else { + return null; + } + } + return field; + } catch (NoSuchFieldException e) { // NOPMD + // ignore + } + return null; + } + + /** + * Reads an accessible static Field. + * @param field to read + * @return the field value + * @throws IllegalArgumentException if the field is null or not static + * @throws IllegalAccessException if the field is not accessible + */ + public static Object readStaticField(Field field) throws IllegalAccessException { + return readStaticField(field, false); + } + + /** + * Reads a static Field. + * @param field to read + * @param forceAccess whether to break scope restrictions using the + * setAccessible method. + * @return the field value + * @throws IllegalArgumentException if the field is null or not static + * @throws IllegalAccessException if the field is not made accessible + */ + public static Object readStaticField(Field field, boolean forceAccess) throws IllegalAccessException { + if (field == null) { + throw new IllegalArgumentException("The field must not be null"); + } + if (!Modifier.isStatic(field.getModifiers())) { + throw new IllegalArgumentException("The field '" + field.getName() + "' is not static"); + } + return readField(field, (Object) null, forceAccess); + } + + /** + * Reads the named public static field. Superclasses will be considered. + * @param cls the class to reflect, must not be null + * @param fieldName the field name to obtain + * @return the value of the field + * @throws IllegalArgumentException if the class is null, the field name is null or if the field could not be found + * @throws IllegalAccessException if the field is not accessible + */ + public static Object readStaticField(Class cls, String fieldName) throws IllegalAccessException { + return readStaticField(cls, fieldName, false); + } + + /** + * Reads the named static field. Superclasses will be considered. + * @param cls the class to reflect, must not be null + * @param fieldName the field name to obtain + * @param forceAccess whether to break scope restrictions using the + * setAccessible method. False will only + * match public fields. + * @return the Field object + * @throws IllegalArgumentException if the class is null, the field name is null or if the field could not be found + * @throws IllegalAccessException if the field is not made accessible + */ + public static Object readStaticField(Class cls, String fieldName, boolean forceAccess) + throws IllegalAccessException { + Field field = getField(cls, fieldName, forceAccess); + if (field == null) { + throw new IllegalArgumentException("Cannot locate field " + fieldName + " on " + cls); + } + //already forced access above, don't repeat it here: + return readStaticField(field, false); + } + + /** + * Gets a static Field value by name. The field must be public. + * Only the specified class will be considered. + * + * @param cls the class to reflect, must not be null + * @param fieldName the field name to obtain + * @return the value of the field + * @throws IllegalArgumentException if the class is null, the field name is null or if the field could not be found + * @throws IllegalAccessException if the field is not accessible + */ + public static Object readDeclaredStaticField(Class cls, String fieldName) throws IllegalAccessException { + return readDeclaredStaticField(cls, fieldName, false); + } + + /** + * Gets a static Field value by name. Only the specified class will + * be considered. + * + * @param cls the class to reflect, must not be null + * @param fieldName the field name to obtain + * @param forceAccess whether to break scope restrictions using the + * setAccessible method. False will only + * match public fields. + * @return the Field object + * @throws IllegalArgumentException if the class is null, the field name is null or if the field could not be found + * @throws IllegalAccessException if the field is not made accessible + */ + public static Object readDeclaredStaticField(Class cls, String fieldName, boolean forceAccess) + throws IllegalAccessException { + Field field = getDeclaredField(cls, fieldName, forceAccess); + if (field == null) { + throw new IllegalArgumentException("Cannot locate declared field " + cls.getName() + "." + fieldName); + } + //already forced access above, don't repeat it here: + return readStaticField(field, false); + } + + /** + * Reads an accessible Field. + * @param field the field to use + * @param target the object to call on, may be null for static fields + * @return the field value + * @throws IllegalArgumentException if the field is null + * @throws IllegalAccessException if the field is not accessible + */ + public static Object readField(Field field, Object target) throws IllegalAccessException { + return readField(field, target, false); + } + + /** + * Reads a Field. + * @param field the field to use + * @param target the object to call on, may be null for static fields + * @param forceAccess whether to break scope restrictions using the + * setAccessible method. + * @return the field value + * @throws IllegalArgumentException if the field is null + * @throws IllegalAccessException if the field is not made accessible + */ + public static Object readField(Field field, Object target, boolean forceAccess) throws IllegalAccessException { + if (field == null) { + throw new IllegalArgumentException("The field must not be null"); + } + if (forceAccess && !field.isAccessible()) { + field.setAccessible(true); + } else { + MemberUtils.setAccessibleWorkaround(field); + } + return field.get(target); + } + + /** + * Reads the named public field. Superclasses will be considered. + * @param target the object to reflect, must not be null + * @param fieldName the field name to obtain + * @return the value of the field + * @throws IllegalArgumentException if the class or field name is null + * @throws IllegalAccessException if the named field is not public + */ + public static Object readField(Object target, String fieldName) throws IllegalAccessException { + return readField(target, fieldName, false); + } + + /** + * Reads the named field. Superclasses will be considered. + * @param target the object to reflect, must not be null + * @param fieldName the field name to obtain + * @param forceAccess whether to break scope restrictions using the + * setAccessible method. False will only + * match public fields. + * @return the field value + * @throws IllegalArgumentException if the class or field name is null + * @throws IllegalAccessException if the named field is not made accessible + */ + public static Object readField(Object target, String fieldName, boolean forceAccess) throws IllegalAccessException { + if (target == null) { + throw new IllegalArgumentException("target object must not be null"); + } + Class cls = target.getClass(); + Field field = getField(cls, fieldName, forceAccess); + if (field == null) { + throw new IllegalArgumentException("Cannot locate field " + fieldName + " on " + cls); + } + //already forced access above, don't repeat it here: + return readField(field, target); + } + + /** + * Reads the named public field. Only the class of the specified object will be considered. + * @param target the object to reflect, must not be null + * @param fieldName the field name to obtain + * @return the value of the field + * @throws IllegalArgumentException if the class or field name is null + * @throws IllegalAccessException if the named field is not public + */ + public static Object readDeclaredField(Object target, String fieldName) throws IllegalAccessException { + return readDeclaredField(target, fieldName, false); + } + + /** + * Gets a Field value by name. Only the class of the specified + * object will be considered. + * + * @param target the object to reflect, must not be null + * @param fieldName the field name to obtain + * @param forceAccess whether to break scope restrictions using the + * setAccessible method. False will only + * match public fields. + * @return the Field object + * @throws IllegalArgumentException if target or fieldName is null + * @throws IllegalAccessException if the field is not made accessible + */ + public static Object readDeclaredField(Object target, String fieldName, boolean forceAccess) + throws IllegalAccessException { + if (target == null) { + throw new IllegalArgumentException("target object must not be null"); + } + Class cls = target.getClass(); + Field field = getDeclaredField(cls, fieldName, forceAccess); + if (field == null) { + throw new IllegalArgumentException("Cannot locate declared field " + cls.getName() + "." + fieldName); + } + //already forced access above, don't repeat it here: + return readField(field, target); + } + + /** + * Writes a public static Field. + * @param field to write + * @param value to set + * @throws IllegalArgumentException if the field is null or not static + * @throws IllegalAccessException if the field is not public or is final + */ + public static void writeStaticField(Field field, Object value) throws IllegalAccessException { + writeStaticField(field, value, false); + } + + /** + * Writes a static Field. + * @param field to write + * @param value to set + * @param forceAccess whether to break scope restrictions using the + * setAccessible method. False will only + * match public fields. + * @throws IllegalArgumentException if the field is null or not static + * @throws IllegalAccessException if the field is not made accessible or is final + */ + public static void writeStaticField(Field field, Object value, boolean forceAccess) throws IllegalAccessException { + if (field == null) { + throw new IllegalArgumentException("The field must not be null"); + } + if (!Modifier.isStatic(field.getModifiers())) { + throw new IllegalArgumentException("The field '" + field.getName() + "' is not static"); + } + writeField(field, (Object) null, value, forceAccess); + } + + /** + * Writes a named public static Field. Superclasses will be considered. + * @param cls Class on which the Field is to be found + * @param fieldName to write + * @param value to set + * @throws IllegalArgumentException if the field cannot be located or is not static + * @throws IllegalAccessException if the field is not public or is final + */ + public static void writeStaticField(Class cls, String fieldName, Object value) throws IllegalAccessException { + writeStaticField(cls, fieldName, value, false); + } + + /** + * Writes a named static Field. Superclasses will be considered. + * @param cls Class on which the Field is to be found + * @param fieldName to write + * @param value to set + * @param forceAccess whether to break scope restrictions using the + * setAccessible method. False will only + * match public fields. + * @throws IllegalArgumentException if the field cannot be located or is not static + * @throws IllegalAccessException if the field is not made accessible or is final + */ + public static void writeStaticField(Class cls, String fieldName, Object value, boolean forceAccess) + throws IllegalAccessException { + Field field = getField(cls, fieldName, forceAccess); + if (field == null) { + throw new IllegalArgumentException("Cannot locate field " + fieldName + " on " + cls); + } + //already forced access above, don't repeat it here: + writeStaticField(field, value); + } + + /** + * Writes a named public static Field. Only the specified class will be considered. + * @param cls Class on which the Field is to be found + * @param fieldName to write + * @param value to set + * @throws IllegalArgumentException if the field cannot be located or is not static + * @throws IllegalAccessException if the field is not public or is final + */ + public static void writeDeclaredStaticField(Class cls, String fieldName, Object value) + throws IllegalAccessException { + writeDeclaredStaticField(cls, fieldName, value, false); + } + + /** + * Writes a named static Field. Only the specified class will be considered. + * @param cls Class on which the Field is to be found + * @param fieldName to write + * @param value to set + * @param forceAccess whether to break scope restrictions using the + * setAccessible method. False will only + * match public fields. + * @throws IllegalArgumentException if the field cannot be located or is not static + * @throws IllegalAccessException if the field is not made accessible or is final + */ + public static void writeDeclaredStaticField(Class cls, String fieldName, Object value, boolean forceAccess) + throws IllegalAccessException { + Field field = getDeclaredField(cls, fieldName, forceAccess); + if (field == null) { + throw new IllegalArgumentException("Cannot locate declared field " + cls.getName() + "." + fieldName); + } + //already forced access above, don't repeat it here: + writeField(field, (Object) null, value); + } + + /** + * Writes an accessible field. + * @param field to write + * @param target the object to call on, may be null for static fields + * @param value to set + * @throws IllegalArgumentException if the field is null + * @throws IllegalAccessException if the field is not accessible or is final + */ + public static void writeField(Field field, Object target, Object value) throws IllegalAccessException { + writeField(field, target, value, false); + } + + /** + * Writes a field. + * @param field to write + * @param target the object to call on, may be null for static fields + * @param value to set + * @param forceAccess whether to break scope restrictions using the + * setAccessible method. False will only + * match public fields. + * @throws IllegalArgumentException if the field is null + * @throws IllegalAccessException if the field is not made accessible or is final + */ + public static void writeField(Field field, Object target, Object value, boolean forceAccess) + throws IllegalAccessException { + if (field == null) { + throw new IllegalArgumentException("The field must not be null"); + } + if (forceAccess && !field.isAccessible()) { + field.setAccessible(true); + } else { + MemberUtils.setAccessibleWorkaround(field); + } + field.set(target, value); + } + + /** + * Writes a public field. Superclasses will be considered. + * @param target the object to reflect, must not be null + * @param fieldName the field name to obtain + * @param value to set + * @throws IllegalArgumentException if target or fieldName is null + * @throws IllegalAccessException if the field is not accessible + */ + public static void writeField(Object target, String fieldName, Object value) throws IllegalAccessException { + writeField(target, fieldName, value, false); + } + + /** + * Writes a field. Superclasses will be considered. + * @param target the object to reflect, must not be null + * @param fieldName the field name to obtain + * @param value to set + * @param forceAccess whether to break scope restrictions using the + * setAccessible method. False will only + * match public fields. + * @throws IllegalArgumentException if target or fieldName is null + * @throws IllegalAccessException if the field is not made accessible + */ + public static void writeField(Object target, String fieldName, Object value, boolean forceAccess) + throws IllegalAccessException { + if (target == null) { + throw new IllegalArgumentException("target object must not be null"); + } + Class cls = target.getClass(); + Field field = getField(cls, fieldName, forceAccess); + if (field == null) { + throw new IllegalArgumentException("Cannot locate declared field " + cls.getName() + "." + fieldName); + } + //already forced access above, don't repeat it here: + writeField(field, target, value); + } + + /** + * Writes a public field. Only the specified class will be considered. + * @param target the object to reflect, must not be null + * @param fieldName the field name to obtain + * @param value to set + * @throws IllegalArgumentException if target or fieldName is null + * @throws IllegalAccessException if the field is not made accessible + */ + public static void writeDeclaredField(Object target, String fieldName, Object value) throws IllegalAccessException { + writeDeclaredField(target, fieldName, value, false); + } + + /** + * Writes a public field. Only the specified class will be considered. + * @param target the object to reflect, must not be null + * @param fieldName the field name to obtain + * @param value to set + * @param forceAccess whether to break scope restrictions using the + * setAccessible method. False will only + * match public fields. + * @throws IllegalArgumentException if target or fieldName is null + * @throws IllegalAccessException if the field is not made accessible + */ + public static void writeDeclaredField(Object target, String fieldName, Object value, boolean forceAccess) + throws IllegalAccessException { + if (target == null) { + throw new IllegalArgumentException("target object must not be null"); + } + Class cls = target.getClass(); + Field field = getDeclaredField(cls, fieldName, forceAccess); + if (field == null) { + throw new IllegalArgumentException("Cannot locate declared field " + cls.getName() + "." + fieldName); + } + //already forced access above, don't repeat it here: + writeField(field, target, value); + } +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/reflect/FieldUtilsTest.java b/ApacheCommonsLang/org/apache/commons/lang3/reflect/FieldUtilsTest.java new file mode 100644 index 0000000..8a2a932 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/reflect/FieldUtilsTest.java @@ -0,0 +1,1143 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.reflect; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; +import static org.junit.Assume.assumeNotNull; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; + +import org.apache.commons.lang3.reflect.testbed.Ambig; +import org.apache.commons.lang3.reflect.testbed.Foo; +import org.apache.commons.lang3.reflect.testbed.PrivatelyShadowedChild; +import org.apache.commons.lang3.reflect.testbed.PublicChild; +import org.apache.commons.lang3.reflect.testbed.PubliclyShadowedChild; +import org.apache.commons.lang3.reflect.testbed.StaticContainer; +import org.apache.commons.lang3.reflect.testbed.StaticContainerChild; +import org.junit.Before; +import org.junit.Test; + +/** + * Unit tests FieldUtils + * @version $Id: FieldUtilsTest.java 1199724 2011-11-09 12:51:52Z sebb $ + */ +public class FieldUtilsTest { + + static final String S = "s"; + static final String SS = "ss"; + static final Integer I0 = Integer.valueOf(0); + static final Integer I1 = Integer.valueOf(1); + static final Double D0 = Double.valueOf(0.0); + static final Double D1 = Double.valueOf(1.0); + + private PublicChild publicChild; + private PubliclyShadowedChild publiclyShadowedChild; + private PrivatelyShadowedChild privatelyShadowedChild; + private Class parentClass = PublicChild.class.getSuperclass(); + + @Before + public void setUp() { + StaticContainer.reset(); + publicChild = new PublicChild(); + publiclyShadowedChild = new PubliclyShadowedChild(); + privatelyShadowedChild = new PrivatelyShadowedChild(); + } + + @Test + public void testConstructor() { + assertNotNull(new FieldUtils()); + Constructor[] cons = FieldUtils.class.getDeclaredConstructors(); + assertEquals(1, cons.length); + assertTrue(Modifier.isPublic(cons[0].getModifiers())); + assertTrue(Modifier.isPublic(FieldUtils.class.getModifiers())); + assertFalse(Modifier.isFinal(FieldUtils.class.getModifiers())); + } + + @Test + public void testGetField() { + assertEquals(Foo.class, FieldUtils.getField(PublicChild.class, "VALUE").getDeclaringClass()); + assertEquals(parentClass, FieldUtils.getField(PublicChild.class, "s").getDeclaringClass()); + assertNull(FieldUtils.getField(PublicChild.class, "b")); + assertNull(FieldUtils.getField(PublicChild.class, "i")); + assertNull(FieldUtils.getField(PublicChild.class, "d")); + assertEquals(Foo.class, FieldUtils.getField(PubliclyShadowedChild.class, "VALUE").getDeclaringClass()); + assertEquals(PubliclyShadowedChild.class, FieldUtils.getField(PubliclyShadowedChild.class, "s") + .getDeclaringClass()); + assertEquals(PubliclyShadowedChild.class, FieldUtils.getField(PubliclyShadowedChild.class, "b") + .getDeclaringClass()); + assertEquals(PubliclyShadowedChild.class, FieldUtils.getField(PubliclyShadowedChild.class, "i") + .getDeclaringClass()); + assertEquals(PubliclyShadowedChild.class, FieldUtils.getField(PubliclyShadowedChild.class, "d") + .getDeclaringClass()); + assertEquals(Foo.class, FieldUtils.getField(PrivatelyShadowedChild.class, "VALUE").getDeclaringClass()); + assertEquals(parentClass, FieldUtils.getField(PrivatelyShadowedChild.class, "s").getDeclaringClass()); + assertNull(FieldUtils.getField(PrivatelyShadowedChild.class, "b")); + assertNull(FieldUtils.getField(PrivatelyShadowedChild.class, "i")); + assertNull(FieldUtils.getField(PrivatelyShadowedChild.class, "d")); + } + + @Test(expected=IllegalArgumentException.class) + public void testGetFieldIllegalArgumentException1() { + FieldUtils.getField(null, "none"); + } + + @Test(expected=IllegalArgumentException.class) + public void testGetFieldIllegalArgumentException2() { + FieldUtils.getField(PublicChild.class, null); + } + + @Test + public void testGetFieldForceAccess() { + assertEquals(PublicChild.class, FieldUtils.getField(PublicChild.class, "VALUE", true).getDeclaringClass()); + assertEquals(parentClass, FieldUtils.getField(PublicChild.class, "s", true).getDeclaringClass()); + assertEquals(parentClass, FieldUtils.getField(PublicChild.class, "b", true).getDeclaringClass()); + assertEquals(parentClass, FieldUtils.getField(PublicChild.class, "i", true).getDeclaringClass()); + assertEquals(parentClass, FieldUtils.getField(PublicChild.class, "d", true).getDeclaringClass()); + assertEquals(Foo.class, FieldUtils.getField(PubliclyShadowedChild.class, "VALUE", true).getDeclaringClass()); + assertEquals(PubliclyShadowedChild.class, FieldUtils.getField(PubliclyShadowedChild.class, "s", true) + .getDeclaringClass()); + assertEquals(PubliclyShadowedChild.class, FieldUtils.getField(PubliclyShadowedChild.class, "b", true) + .getDeclaringClass()); + assertEquals(PubliclyShadowedChild.class, FieldUtils.getField(PubliclyShadowedChild.class, "i", true) + .getDeclaringClass()); + assertEquals(PubliclyShadowedChild.class, FieldUtils.getField(PubliclyShadowedChild.class, "d", true) + .getDeclaringClass()); + assertEquals(Foo.class, FieldUtils.getField(PrivatelyShadowedChild.class, "VALUE", true).getDeclaringClass()); + assertEquals(PrivatelyShadowedChild.class, FieldUtils.getField(PrivatelyShadowedChild.class, "s", true) + .getDeclaringClass()); + assertEquals(PrivatelyShadowedChild.class, FieldUtils.getField(PrivatelyShadowedChild.class, "b", true) + .getDeclaringClass()); + assertEquals(PrivatelyShadowedChild.class, FieldUtils.getField(PrivatelyShadowedChild.class, "i", true) + .getDeclaringClass()); + assertEquals(PrivatelyShadowedChild.class, FieldUtils.getField(PrivatelyShadowedChild.class, "d", true) + .getDeclaringClass()); + } + + @Test(expected=IllegalArgumentException.class) + public void testGetFieldForceAccessIllegalArgumentException1() { + FieldUtils.getField(null, "none", true); + } + + @Test(expected=IllegalArgumentException.class) + public void testGetFieldForceAccessIllegalArgumentException2() { + FieldUtils.getField(PublicChild.class, null, true); + } + + @Test + public void testGetDeclaredField() { + assertNull(FieldUtils.getDeclaredField(PublicChild.class, "VALUE")); + assertNull(FieldUtils.getDeclaredField(PublicChild.class, "s")); + assertNull(FieldUtils.getDeclaredField(PublicChild.class, "b")); + assertNull(FieldUtils.getDeclaredField(PublicChild.class, "i")); + assertNull(FieldUtils.getDeclaredField(PublicChild.class, "d")); + assertNull(FieldUtils.getDeclaredField(PubliclyShadowedChild.class, "VALUE")); + assertEquals(PubliclyShadowedChild.class, FieldUtils.getDeclaredField(PubliclyShadowedChild.class, "s") + .getDeclaringClass()); + assertEquals(PubliclyShadowedChild.class, FieldUtils.getDeclaredField(PubliclyShadowedChild.class, "b") + .getDeclaringClass()); + assertEquals(PubliclyShadowedChild.class, FieldUtils.getDeclaredField(PubliclyShadowedChild.class, "i") + .getDeclaringClass()); + assertEquals(PubliclyShadowedChild.class, FieldUtils.getDeclaredField(PubliclyShadowedChild.class, "d") + .getDeclaringClass()); + assertNull(FieldUtils.getDeclaredField(PrivatelyShadowedChild.class, "VALUE")); + assertNull(FieldUtils.getDeclaredField(PrivatelyShadowedChild.class, "s")); + assertNull(FieldUtils.getDeclaredField(PrivatelyShadowedChild.class, "b")); + assertNull(FieldUtils.getDeclaredField(PrivatelyShadowedChild.class, "i")); + assertNull(FieldUtils.getDeclaredField(PrivatelyShadowedChild.class, "d")); + } + + @Test(expected=IllegalArgumentException.class) + public void testGetDeclaredFieldAccessIllegalArgumentException1() { + FieldUtils.getDeclaredField(null, "none"); + } + + @Test(expected=IllegalArgumentException.class) + public void testGetDeclaredFieldAccessIllegalArgumentException2() { + FieldUtils.getDeclaredField(PublicChild.class, null); + } + + @Test + public void testGetDeclaredFieldForceAccess() { + assertEquals(PublicChild.class, FieldUtils.getDeclaredField(PublicChild.class, "VALUE", true) + .getDeclaringClass()); + assertNull(FieldUtils.getDeclaredField(PublicChild.class, "s", true)); + assertNull(FieldUtils.getDeclaredField(PublicChild.class, "b", true)); + assertNull(FieldUtils.getDeclaredField(PublicChild.class, "i", true)); + assertNull(FieldUtils.getDeclaredField(PublicChild.class, "d", true)); + assertNull(FieldUtils.getDeclaredField(PubliclyShadowedChild.class, "VALUE", true)); + assertEquals(PubliclyShadowedChild.class, FieldUtils.getDeclaredField(PubliclyShadowedChild.class, "s", true) + .getDeclaringClass()); + assertEquals(PubliclyShadowedChild.class, FieldUtils.getDeclaredField(PubliclyShadowedChild.class, "b", true) + .getDeclaringClass()); + assertEquals(PubliclyShadowedChild.class, FieldUtils.getDeclaredField(PubliclyShadowedChild.class, "i", true) + .getDeclaringClass()); + assertEquals(PubliclyShadowedChild.class, FieldUtils.getDeclaredField(PubliclyShadowedChild.class, "d", true) + .getDeclaringClass()); + assertNull(FieldUtils.getDeclaredField(PrivatelyShadowedChild.class, "VALUE", true)); + assertEquals(PrivatelyShadowedChild.class, FieldUtils.getDeclaredField(PrivatelyShadowedChild.class, "s", true) + .getDeclaringClass()); + assertEquals(PrivatelyShadowedChild.class, FieldUtils.getDeclaredField(PrivatelyShadowedChild.class, "b", true) + .getDeclaringClass()); + assertEquals(PrivatelyShadowedChild.class, FieldUtils.getDeclaredField(PrivatelyShadowedChild.class, "i", true) + .getDeclaringClass()); + assertEquals(PrivatelyShadowedChild.class, FieldUtils.getDeclaredField(PrivatelyShadowedChild.class, "d", true) + .getDeclaringClass()); + } + + @Test(expected=IllegalArgumentException.class) + public void testGetDeclaredFieldForceAccessIllegalArgumentException1() { + FieldUtils.getDeclaredField(null, "none", true); + } + + @Test(expected=IllegalArgumentException.class) + public void testGetDeclaredFieldForceAccessIllegalArgumentException2() { + FieldUtils.getDeclaredField(PublicChild.class, null, true); + } + + @Test + public void testReadStaticField() throws Exception { + assertEquals(Foo.VALUE, FieldUtils.readStaticField(FieldUtils.getField(Foo.class, "VALUE"))); + } + + @Test(expected=IllegalArgumentException.class) + public void testReadStaticFieldIllegalArgumentException1() throws Exception { + FieldUtils.readStaticField(null); + } + + @Test(expected=IllegalArgumentException.class) + public void testReadStaticFieldIllegalArgumentException2() throws Exception { + assertEquals(Foo.VALUE, FieldUtils.readStaticField(FieldUtils.getField(Foo.class, "VALUE"))); + Field nonStaticField = FieldUtils.getField(PublicChild.class, "s"); + assumeNotNull(nonStaticField); + FieldUtils.readStaticField(nonStaticField); + } + + @Test + public void testReadStaticFieldForceAccess() throws Exception { + assertEquals(Foo.VALUE, FieldUtils.readStaticField(FieldUtils.getField(Foo.class, "VALUE"))); + assertEquals(Foo.VALUE, FieldUtils.readStaticField(FieldUtils.getField(PublicChild.class, "VALUE"))); + } + + @Test(expected=IllegalArgumentException.class) + public void testReadStaticFieldForceAccessIllegalArgumentException1() throws Exception { + FieldUtils.readStaticField(null, true); + } + + @Test(expected=IllegalArgumentException.class) + public void testReadStaticFieldForceAccessIllegalArgumentException2() throws Exception { + Field nonStaticField = FieldUtils.getField(PublicChild.class, "s", true); + assumeNotNull(nonStaticField); + FieldUtils.readStaticField(nonStaticField); + } + + @Test + public void testReadNamedStaticField() throws Exception { + assertEquals(Foo.VALUE, FieldUtils.readStaticField(Foo.class, "VALUE")); + assertEquals(Foo.VALUE, FieldUtils.readStaticField(PubliclyShadowedChild.class, "VALUE")); + assertEquals(Foo.VALUE, FieldUtils.readStaticField(PrivatelyShadowedChild.class, "VALUE")); + assertEquals(Foo.VALUE, FieldUtils.readStaticField(PublicChild.class, "VALUE")); + + try { + FieldUtils.readStaticField(null, "none"); + fail("null class should cause an IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // expected + } + + try { + FieldUtils.readStaticField(Foo.class, null); + fail("null field name should cause an IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // expected + } + + try { + FieldUtils.readStaticField(Foo.class, "does_not_exist"); + fail("a field that doesn't exist should cause an IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // expected + } + + try { + FieldUtils.readStaticField(PublicChild.class, "s"); + fail("non-static field should cause an IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // expected + } + } + + @Test + public void testReadNamedStaticFieldForceAccess() throws Exception { + assertEquals(Foo.VALUE, FieldUtils.readStaticField(Foo.class, "VALUE", true)); + assertEquals(Foo.VALUE, FieldUtils.readStaticField(PubliclyShadowedChild.class, "VALUE", true)); + assertEquals(Foo.VALUE, FieldUtils.readStaticField(PrivatelyShadowedChild.class, "VALUE", true)); + assertEquals("child", FieldUtils.readStaticField(PublicChild.class, "VALUE", true)); + + try { + FieldUtils.readStaticField(null, "none", true); + fail("null class should cause an IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // expected + } + + try { + FieldUtils.readStaticField(Foo.class, null, true); + fail("null field name should cause an IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // expected + } + + try { + FieldUtils.readStaticField(Foo.class, "does_not_exist", true); + fail("a field that doesn't exist should cause an IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // expected + } + + try { + FieldUtils.readStaticField(PublicChild.class, "s", false); + fail("non-static field should cause an IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // expected + } + } + + @Test + public void testReadDeclaredNamedStaticField() throws Exception { + assertEquals(Foo.VALUE, FieldUtils.readDeclaredStaticField(Foo.class, "VALUE")); + try { + assertEquals("child", FieldUtils.readDeclaredStaticField(PublicChild.class, "VALUE")); + fail("expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // pass + } + try { + assertEquals(Foo.VALUE, FieldUtils.readDeclaredStaticField(PubliclyShadowedChild.class, "VALUE")); + fail("expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // pass + } + try { + assertEquals(Foo.VALUE, FieldUtils.readDeclaredStaticField(PrivatelyShadowedChild.class, "VALUE")); + fail("expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // pass + } + } + + @Test + public void testReadDeclaredNamedStaticFieldForceAccess() throws Exception { + assertEquals(Foo.VALUE, FieldUtils.readDeclaredStaticField(Foo.class, "VALUE", true)); + assertEquals("child", FieldUtils.readDeclaredStaticField(PublicChild.class, "VALUE", true)); + try { + assertEquals(Foo.VALUE, FieldUtils.readDeclaredStaticField(PubliclyShadowedChild.class, "VALUE", true)); + fail("expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // pass + } + try { + assertEquals(Foo.VALUE, FieldUtils.readDeclaredStaticField(PrivatelyShadowedChild.class, "VALUE", true)); + fail("expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // pass + } + } + + @Test + public void testReadField() throws Exception { + Field parentS = FieldUtils.getDeclaredField(parentClass, "s"); + assertEquals("s", FieldUtils.readField(parentS, publicChild)); + assertEquals("s", FieldUtils.readField(parentS, publiclyShadowedChild)); + assertEquals("s", FieldUtils.readField(parentS, privatelyShadowedChild)); + Field parentB = FieldUtils.getDeclaredField(parentClass, "b", true); + assertEquals(Boolean.FALSE, FieldUtils.readField(parentB, publicChild)); + assertEquals(Boolean.FALSE, FieldUtils.readField(parentB, publiclyShadowedChild)); + assertEquals(Boolean.FALSE, FieldUtils.readField(parentB, privatelyShadowedChild)); + Field parentI = FieldUtils.getDeclaredField(parentClass, "i", true); + assertEquals(I0, FieldUtils.readField(parentI, publicChild)); + assertEquals(I0, FieldUtils.readField(parentI, publiclyShadowedChild)); + assertEquals(I0, FieldUtils.readField(parentI, privatelyShadowedChild)); + Field parentD = FieldUtils.getDeclaredField(parentClass, "d", true); + assertEquals(D0, FieldUtils.readField(parentD, publicChild)); + assertEquals(D0, FieldUtils.readField(parentD, publiclyShadowedChild)); + assertEquals(D0, FieldUtils.readField(parentD, privatelyShadowedChild)); + + try { + FieldUtils.readField((Field)null, publicChild); + fail("a null field should cause an IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // expected + } + } + + @Test + public void testReadFieldForceAccess() throws Exception { + Field parentS = FieldUtils.getDeclaredField(parentClass, "s"); + parentS.setAccessible(false); + assertEquals("s", FieldUtils.readField(parentS, publicChild, true)); + assertEquals("s", FieldUtils.readField(parentS, publiclyShadowedChild, true)); + assertEquals("s", FieldUtils.readField(parentS, privatelyShadowedChild, true)); + Field parentB = FieldUtils.getDeclaredField(parentClass, "b", true); + parentB.setAccessible(false); + assertEquals(Boolean.FALSE, FieldUtils.readField(parentB, publicChild, true)); + assertEquals(Boolean.FALSE, FieldUtils.readField(parentB, publiclyShadowedChild, true)); + assertEquals(Boolean.FALSE, FieldUtils.readField(parentB, privatelyShadowedChild, true)); + Field parentI = FieldUtils.getDeclaredField(parentClass, "i", true); + parentI.setAccessible(false); + assertEquals(I0, FieldUtils.readField(parentI, publicChild, true)); + assertEquals(I0, FieldUtils.readField(parentI, publiclyShadowedChild, true)); + assertEquals(I0, FieldUtils.readField(parentI, privatelyShadowedChild, true)); + Field parentD = FieldUtils.getDeclaredField(parentClass, "d", true); + parentD.setAccessible(false); + assertEquals(D0, FieldUtils.readField(parentD, publicChild, true)); + assertEquals(D0, FieldUtils.readField(parentD, publiclyShadowedChild, true)); + assertEquals(D0, FieldUtils.readField(parentD, privatelyShadowedChild, true)); + + try { + FieldUtils.readField((Field)null, publicChild, true); + fail("a null field should cause an IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // expected + } + } + + @Test + public void testReadNamedField() throws Exception { + assertEquals("s", FieldUtils.readField(publicChild, "s")); + assertEquals("ss", FieldUtils.readField(publiclyShadowedChild, "s")); + assertEquals("s", FieldUtils.readField(privatelyShadowedChild, "s")); + + try { + FieldUtils.readField(publicChild, null); + fail("a null field name should cause an IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // expected + } + + try { + FieldUtils.readField((Object)null, "none"); + fail("a null target should cause an IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // expected + } + + try { + assertEquals(Boolean.FALSE, FieldUtils.readField(publicChild, "b")); + fail("expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // pass + } + assertEquals(Boolean.TRUE, FieldUtils.readField(publiclyShadowedChild, "b")); + try { + assertEquals(Boolean.FALSE, FieldUtils.readField(privatelyShadowedChild, "b")); + fail("expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // pass + } + try { + assertEquals(I0, FieldUtils.readField(publicChild, "i")); + fail("expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // pass + } + assertEquals(I1, FieldUtils.readField(publiclyShadowedChild, "i")); + try { + assertEquals(I0, FieldUtils.readField(privatelyShadowedChild, "i")); + fail("expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // pass + } + try { + assertEquals(D0, FieldUtils.readField(publicChild, "d")); + fail("expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // pass + } + assertEquals(D1, FieldUtils.readField(publiclyShadowedChild, "d")); + try { + assertEquals(D0, FieldUtils.readField(privatelyShadowedChild, "d")); + fail("expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // pass + } + } + + @Test + public void testReadNamedFieldForceAccess() throws Exception { + assertEquals("s", FieldUtils.readField(publicChild, "s", true)); + assertEquals("ss", FieldUtils.readField(publiclyShadowedChild, "s", true)); + assertEquals("ss", FieldUtils.readField(privatelyShadowedChild, "s", true)); + assertEquals(Boolean.FALSE, FieldUtils.readField(publicChild, "b", true)); + assertEquals(Boolean.TRUE, FieldUtils.readField(publiclyShadowedChild, "b", true)); + assertEquals(Boolean.TRUE, FieldUtils.readField(privatelyShadowedChild, "b", true)); + assertEquals(I0, FieldUtils.readField(publicChild, "i", true)); + assertEquals(I1, FieldUtils.readField(publiclyShadowedChild, "i", true)); + assertEquals(I1, FieldUtils.readField(privatelyShadowedChild, "i", true)); + assertEquals(D0, FieldUtils.readField(publicChild, "d", true)); + assertEquals(D1, FieldUtils.readField(publiclyShadowedChild, "d", true)); + assertEquals(D1, FieldUtils.readField(privatelyShadowedChild, "d", true)); + + try { + FieldUtils.readField(publicChild, null, true); + fail("a null field name should cause an IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // expected + } + + try { + FieldUtils.readField((Object)null, "none", true); + fail("a null target should cause an IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // expected + } + } + + @Test + public void testReadDeclaredNamedField() throws Exception { + try { + FieldUtils.readDeclaredField(publicChild, null); + fail("a null field name should cause an IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // expected + } + + try { + FieldUtils.readDeclaredField((Object)null, "none"); + fail("a null target should cause an IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // expected + } + + try { + assertEquals("s", FieldUtils.readDeclaredField(publicChild, "s")); + fail("expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // pass + } + assertEquals("ss", FieldUtils.readDeclaredField(publiclyShadowedChild, "s")); + try { + assertEquals("s", FieldUtils.readDeclaredField(privatelyShadowedChild, "s")); + fail("expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // pass + } + try { + assertEquals(Boolean.FALSE, FieldUtils.readDeclaredField(publicChild, "b")); + fail("expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // pass + } + assertEquals(Boolean.TRUE, FieldUtils.readDeclaredField(publiclyShadowedChild, "b")); + try { + assertEquals(Boolean.FALSE, FieldUtils.readDeclaredField(privatelyShadowedChild, "b")); + fail("expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // pass + } + try { + assertEquals(I0, FieldUtils.readDeclaredField(publicChild, "i")); + fail("expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // pass + } + assertEquals(I1, FieldUtils.readDeclaredField(publiclyShadowedChild, "i")); + try { + assertEquals(I0, FieldUtils.readDeclaredField(privatelyShadowedChild, "i")); + fail("expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // pass + } + try { + assertEquals(D0, FieldUtils.readDeclaredField(publicChild, "d")); + fail("expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // pass + } + assertEquals(D1, FieldUtils.readDeclaredField(publiclyShadowedChild, "d")); + try { + assertEquals(D0, FieldUtils.readDeclaredField(privatelyShadowedChild, "d")); + fail("expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // pass + } + } + + @Test + public void testReadDeclaredNamedFieldForceAccess() throws Exception { + try { + FieldUtils.readDeclaredField(publicChild, null, true); + fail("a null field name should cause an IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // expected + } + + try { + FieldUtils.readDeclaredField((Object)null, "none", true); + fail("a null target should cause an IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // expected + } + + try { + assertEquals("s", FieldUtils.readDeclaredField(publicChild, "s", true)); + fail("expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // pass + } + assertEquals("ss", FieldUtils.readDeclaredField(publiclyShadowedChild, "s", true)); + assertEquals("ss", FieldUtils.readDeclaredField(privatelyShadowedChild, "s", true)); + try { + assertEquals(Boolean.FALSE, FieldUtils.readDeclaredField(publicChild, "b", true)); + fail("expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // pass + } + assertEquals(Boolean.TRUE, FieldUtils.readDeclaredField(publiclyShadowedChild, "b", true)); + assertEquals(Boolean.TRUE, FieldUtils.readDeclaredField(privatelyShadowedChild, "b", true)); + try { + assertEquals(I0, FieldUtils.readDeclaredField(publicChild, "i", true)); + fail("expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // pass + } + assertEquals(I1, FieldUtils.readDeclaredField(publiclyShadowedChild, "i", true)); + assertEquals(I1, FieldUtils.readDeclaredField(privatelyShadowedChild, "i", true)); + try { + assertEquals(D0, FieldUtils.readDeclaredField(publicChild, "d", true)); + fail("expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // pass + } + assertEquals(D1, FieldUtils.readDeclaredField(publiclyShadowedChild, "d", true)); + assertEquals(D1, FieldUtils.readDeclaredField(privatelyShadowedChild, "d", true)); + } + + @Test + public void testWriteStaticField() throws Exception { + Field field = StaticContainer.class.getDeclaredField("mutablePublic"); + FieldUtils.writeStaticField(field, "new"); + assertEquals("new", StaticContainer.mutablePublic); + field = StaticContainer.class.getDeclaredField("mutableProtected"); + try { + FieldUtils.writeStaticField(field, "new"); + fail("Expected IllegalAccessException"); + } catch (IllegalAccessException e) { + // pass + } + field = StaticContainer.class.getDeclaredField("mutablePackage"); + try { + FieldUtils.writeStaticField(field, "new"); + fail("Expected IllegalAccessException"); + } catch (IllegalAccessException e) { + // pass + } + field = StaticContainer.class.getDeclaredField("mutablePrivate"); + try { + FieldUtils.writeStaticField(field, "new"); + fail("Expected IllegalAccessException"); + } catch (IllegalAccessException e) { + // pass + } + field = StaticContainer.class.getDeclaredField("IMMUTABLE_PUBLIC"); + try { + FieldUtils.writeStaticField(field, "new"); + fail("Expected IllegalAccessException"); + } catch (IllegalAccessException e) { + // pass + } + field = StaticContainer.class.getDeclaredField("IMMUTABLE_PROTECTED"); + try { + FieldUtils.writeStaticField(field, "new"); + fail("Expected IllegalAccessException"); + } catch (IllegalAccessException e) { + // pass + } + field = StaticContainer.class.getDeclaredField("IMMUTABLE_PACKAGE"); + try { + FieldUtils.writeStaticField(field, "new"); + fail("Expected IllegalAccessException"); + } catch (IllegalAccessException e) { + // pass + } + field = StaticContainer.class.getDeclaredField("IMMUTABLE_PRIVATE"); + try { + FieldUtils.writeStaticField(field, "new"); + fail("Expected IllegalAccessException"); + } catch (IllegalAccessException e) { + // pass + } + } + + @Test + public void testWriteStaticFieldForceAccess() throws Exception { + Field field = StaticContainer.class.getDeclaredField("mutablePublic"); + FieldUtils.writeStaticField(field, "new", true); + assertEquals("new", StaticContainer.mutablePublic); + field = StaticContainer.class.getDeclaredField("mutableProtected"); + FieldUtils.writeStaticField(field, "new", true); + assertEquals("new", StaticContainer.getMutableProtected()); + field = StaticContainer.class.getDeclaredField("mutablePackage"); + FieldUtils.writeStaticField(field, "new", true); + assertEquals("new", StaticContainer.getMutablePackage()); + field = StaticContainer.class.getDeclaredField("mutablePrivate"); + FieldUtils.writeStaticField(field, "new", true); + assertEquals("new", StaticContainer.getMutablePrivate()); + field = StaticContainer.class.getDeclaredField("IMMUTABLE_PUBLIC"); + try { + FieldUtils.writeStaticField(field, "new", true); + fail("Expected IllegalAccessException"); + } catch (IllegalAccessException e) { + // pass + } + field = StaticContainer.class.getDeclaredField("IMMUTABLE_PROTECTED"); + try { + FieldUtils.writeStaticField(field, "new", true); + fail("Expected IllegalAccessException"); + } catch (IllegalAccessException e) { + // pass + } + field = StaticContainer.class.getDeclaredField("IMMUTABLE_PACKAGE"); + try { + FieldUtils.writeStaticField(field, "new", true); + fail("Expected IllegalAccessException"); + } catch (IllegalAccessException e) { + // pass + } + field = StaticContainer.class.getDeclaredField("IMMUTABLE_PRIVATE"); + try { + FieldUtils.writeStaticField(field, "new", true); + fail("Expected IllegalAccessException"); + } catch (IllegalAccessException e) { + // pass + } + } + + @Test + public void testWriteNamedStaticField() throws Exception { + FieldUtils.writeStaticField(StaticContainerChild.class, "mutablePublic", "new"); + assertEquals("new", StaticContainer.mutablePublic); + try { + FieldUtils.writeStaticField(StaticContainerChild.class, "mutableProtected", "new"); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // pass + } + try { + FieldUtils.writeStaticField(StaticContainerChild.class, "mutablePackage", "new"); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // pass + } + try { + FieldUtils.writeStaticField(StaticContainerChild.class, "mutablePrivate", "new"); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // pass + } + try { + FieldUtils.writeStaticField(StaticContainerChild.class, "IMMUTABLE_PUBLIC", "new"); + fail("Expected IllegalAccessException"); + } catch (IllegalAccessException e) { + // pass + } + try { + FieldUtils.writeStaticField(StaticContainerChild.class, "IMMUTABLE_PROTECTED", "new"); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // pass + } + try { + FieldUtils.writeStaticField(StaticContainerChild.class, "IMMUTABLE_PACKAGE", "new"); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // pass + } + try { + FieldUtils.writeStaticField(StaticContainerChild.class, "IMMUTABLE_PRIVATE", "new"); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // pass + } + } + + @Test + public void testWriteNamedStaticFieldForceAccess() throws Exception { + FieldUtils.writeStaticField(StaticContainerChild.class, "mutablePublic", "new", true); + assertEquals("new", StaticContainer.mutablePublic); + FieldUtils.writeStaticField(StaticContainerChild.class, "mutableProtected", "new", true); + assertEquals("new", StaticContainer.getMutableProtected()); + FieldUtils.writeStaticField(StaticContainerChild.class, "mutablePackage", "new", true); + assertEquals("new", StaticContainer.getMutablePackage()); + FieldUtils.writeStaticField(StaticContainerChild.class, "mutablePrivate", "new", true); + assertEquals("new", StaticContainer.getMutablePrivate()); + try { + FieldUtils.writeStaticField(StaticContainerChild.class, "IMMUTABLE_PUBLIC", "new", true); + fail("Expected IllegalAccessException"); + } catch (IllegalAccessException e) { + // pass + } + try { + FieldUtils.writeStaticField(StaticContainerChild.class, "IMMUTABLE_PROTECTED", "new", true); + fail("Expected IllegalAccessException"); + } catch (IllegalAccessException e) { + // pass + } + try { + FieldUtils.writeStaticField(StaticContainerChild.class, "IMMUTABLE_PACKAGE", "new", true); + fail("Expected IllegalAccessException"); + } catch (IllegalAccessException e) { + // pass + } + try { + FieldUtils.writeStaticField(StaticContainerChild.class, "IMMUTABLE_PRIVATE", "new", true); + fail("Expected IllegalAccessException"); + } catch (IllegalAccessException e) { + // pass + } + } + + @Test + public void testWriteDeclaredNamedStaticField() throws Exception { + FieldUtils.writeStaticField(StaticContainer.class, "mutablePublic", "new"); + assertEquals("new", StaticContainer.mutablePublic); + try { + FieldUtils.writeDeclaredStaticField(StaticContainer.class, "mutableProtected", "new"); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // pass + } + try { + FieldUtils.writeDeclaredStaticField(StaticContainer.class, "mutablePackage", "new"); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // pass + } + try { + FieldUtils.writeDeclaredStaticField(StaticContainer.class, "mutablePrivate", "new"); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // pass + } + try { + FieldUtils.writeDeclaredStaticField(StaticContainer.class, "IMMUTABLE_PUBLIC", "new"); + fail("Expected IllegalAccessException"); + } catch (IllegalAccessException e) { + // pass + } + try { + FieldUtils.writeDeclaredStaticField(StaticContainer.class, "IMMUTABLE_PROTECTED", "new"); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // pass + } + try { + FieldUtils.writeDeclaredStaticField(StaticContainer.class, "IMMUTABLE_PACKAGE", "new"); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // pass + } + try { + FieldUtils.writeDeclaredStaticField(StaticContainer.class, "IMMUTABLE_PRIVATE", "new"); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // pass + } + } + + @Test + public void testWriteDeclaredNamedStaticFieldForceAccess() throws Exception { + FieldUtils.writeDeclaredStaticField(StaticContainer.class, "mutablePublic", "new", true); + assertEquals("new", StaticContainer.mutablePublic); + FieldUtils.writeDeclaredStaticField(StaticContainer.class, "mutableProtected", "new", true); + assertEquals("new", StaticContainer.getMutableProtected()); + FieldUtils.writeDeclaredStaticField(StaticContainer.class, "mutablePackage", "new", true); + assertEquals("new", StaticContainer.getMutablePackage()); + FieldUtils.writeDeclaredStaticField(StaticContainer.class, "mutablePrivate", "new", true); + assertEquals("new", StaticContainer.getMutablePrivate()); + try { + FieldUtils.writeDeclaredStaticField(StaticContainer.class, "IMMUTABLE_PUBLIC", "new", true); + fail("Expected IllegalAccessException"); + } catch (IllegalAccessException e) { + // pass + } + try { + FieldUtils.writeDeclaredStaticField(StaticContainer.class, "IMMUTABLE_PROTECTED", "new", true); + fail("Expected IllegalAccessException"); + } catch (IllegalAccessException e) { + // pass + } + try { + FieldUtils.writeDeclaredStaticField(StaticContainer.class, "IMMUTABLE_PACKAGE", "new", true); + fail("Expected IllegalAccessException"); + } catch (IllegalAccessException e) { + // pass + } + try { + FieldUtils.writeDeclaredStaticField(StaticContainer.class, "IMMUTABLE_PRIVATE", "new", true); + fail("Expected IllegalAccessException"); + } catch (IllegalAccessException e) { + // pass + } + } + + @Test + public void testWriteField() throws Exception { + Field field = parentClass.getDeclaredField("s"); + FieldUtils.writeField(field, publicChild, "S"); + assertEquals("S", field.get(publicChild)); + field = parentClass.getDeclaredField("b"); + try { + FieldUtils.writeField(field, publicChild, Boolean.TRUE); + fail("Expected IllegalAccessException"); + } catch (IllegalAccessException e) { + // pass + } + field = parentClass.getDeclaredField("i"); + try { + FieldUtils.writeField(field, publicChild, Integer.valueOf(Integer.MAX_VALUE)); + } catch (IllegalAccessException e) { + // pass + } + field = parentClass.getDeclaredField("d"); + try { + FieldUtils.writeField(field, publicChild, Double.valueOf(Double.MAX_VALUE)); + } catch (IllegalAccessException e) { + // pass + } + } + + @Test + public void testWriteFieldForceAccess() throws Exception { + Field field = parentClass.getDeclaredField("s"); + FieldUtils.writeField(field, publicChild, "S", true); + assertEquals("S", field.get(publicChild)); + field = parentClass.getDeclaredField("b"); + FieldUtils.writeField(field, publicChild, Boolean.TRUE, true); + assertEquals(Boolean.TRUE, field.get(publicChild)); + field = parentClass.getDeclaredField("i"); + FieldUtils.writeField(field, publicChild, Integer.valueOf(Integer.MAX_VALUE), true); + assertEquals(Integer.valueOf(Integer.MAX_VALUE), field.get(publicChild)); + field = parentClass.getDeclaredField("d"); + FieldUtils.writeField(field, publicChild, Double.valueOf(Double.MAX_VALUE), true); + assertEquals(Double.valueOf(Double.MAX_VALUE), field.get(publicChild)); + } + + @Test + public void testWriteNamedField() throws Exception { + FieldUtils.writeField(publicChild, "s", "S"); + assertEquals("S", FieldUtils.readField(publicChild, "s")); + try { + FieldUtils.writeField(publicChild, "b", Boolean.TRUE); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // pass + } + try { + FieldUtils.writeField(publicChild, "i", Integer.valueOf(1)); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // pass + } + try { + FieldUtils.writeField(publicChild, "d", Double.valueOf(1.0)); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // pass + } + + FieldUtils.writeField(publiclyShadowedChild, "s", "S"); + assertEquals("S", FieldUtils.readField(publiclyShadowedChild, "s")); + FieldUtils.writeField(publiclyShadowedChild, "b", Boolean.FALSE); + assertEquals(Boolean.FALSE, FieldUtils.readField(publiclyShadowedChild, "b")); + FieldUtils.writeField(publiclyShadowedChild, "i", Integer.valueOf(0)); + assertEquals(Integer.valueOf(0), FieldUtils.readField(publiclyShadowedChild, "i")); + FieldUtils.writeField(publiclyShadowedChild, "d", Double.valueOf(0.0)); + assertEquals(Double.valueOf(0.0), FieldUtils.readField(publiclyShadowedChild, "d")); + + FieldUtils.writeField(privatelyShadowedChild, "s", "S"); + assertEquals("S", FieldUtils.readField(privatelyShadowedChild, "s")); + try { + FieldUtils.writeField(privatelyShadowedChild, "b", Boolean.TRUE); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // pass + } + try { + FieldUtils.writeField(privatelyShadowedChild, "i", Integer.valueOf(1)); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // pass + } + try { + FieldUtils.writeField(privatelyShadowedChild, "d", Double.valueOf(1.0)); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // pass + } + } + + @Test + public void testWriteNamedFieldForceAccess() throws Exception { + FieldUtils.writeField(publicChild, "s", "S", true); + assertEquals("S", FieldUtils.readField(publicChild, "s", true)); + FieldUtils.writeField(publicChild, "b", Boolean.TRUE, true); + assertEquals(Boolean.TRUE, FieldUtils.readField(publicChild, "b", true)); + FieldUtils.writeField(publicChild, "i", Integer.valueOf(1), true); + assertEquals(Integer.valueOf(1), FieldUtils.readField(publicChild, "i", true)); + FieldUtils.writeField(publicChild, "d", Double.valueOf(1.0), true); + assertEquals(Double.valueOf(1.0), FieldUtils.readField(publicChild, "d", true)); + + FieldUtils.writeField(publiclyShadowedChild, "s", "S", true); + assertEquals("S", FieldUtils.readField(publiclyShadowedChild, "s", true)); + FieldUtils.writeField(publiclyShadowedChild, "b", Boolean.FALSE, true); + assertEquals(Boolean.FALSE, FieldUtils.readField(publiclyShadowedChild, "b", true)); + FieldUtils.writeField(publiclyShadowedChild, "i", Integer.valueOf(0), true); + assertEquals(Integer.valueOf(0), FieldUtils.readField(publiclyShadowedChild, "i", true)); + FieldUtils.writeField(publiclyShadowedChild, "d", Double.valueOf(0.0), true); + assertEquals(Double.valueOf(0.0), FieldUtils.readField(publiclyShadowedChild, "d", true)); + + FieldUtils.writeField(privatelyShadowedChild, "s", "S", true); + assertEquals("S", FieldUtils.readField(privatelyShadowedChild, "s", true)); + FieldUtils.writeField(privatelyShadowedChild, "b", Boolean.FALSE, true); + assertEquals(Boolean.FALSE, FieldUtils.readField(privatelyShadowedChild, "b", true)); + FieldUtils.writeField(privatelyShadowedChild, "i", Integer.valueOf(0), true); + assertEquals(Integer.valueOf(0), FieldUtils.readField(privatelyShadowedChild, "i", true)); + FieldUtils.writeField(privatelyShadowedChild, "d", Double.valueOf(0.0), true); + assertEquals(Double.valueOf(0.0), FieldUtils.readField(privatelyShadowedChild, "d", true)); + } + + @Test + public void testWriteDeclaredNamedField() throws Exception { + try { + FieldUtils.writeDeclaredField(publicChild, "s", "S"); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // pass + } + try { + FieldUtils.writeDeclaredField(publicChild, "b", Boolean.TRUE); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // pass + } + try { + FieldUtils.writeDeclaredField(publicChild, "i", Integer.valueOf(1)); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // pass + } + try { + FieldUtils.writeDeclaredField(publicChild, "d", Double.valueOf(1.0)); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // pass + } + + FieldUtils.writeDeclaredField(publiclyShadowedChild, "s", "S"); + assertEquals("S", FieldUtils.readDeclaredField(publiclyShadowedChild, "s")); + FieldUtils.writeDeclaredField(publiclyShadowedChild, "b", Boolean.FALSE); + assertEquals(Boolean.FALSE, FieldUtils.readDeclaredField(publiclyShadowedChild, "b")); + FieldUtils.writeDeclaredField(publiclyShadowedChild, "i", Integer.valueOf(0)); + assertEquals(Integer.valueOf(0), FieldUtils.readDeclaredField(publiclyShadowedChild, "i")); + FieldUtils.writeDeclaredField(publiclyShadowedChild, "d", Double.valueOf(0.0)); + assertEquals(Double.valueOf(0.0), FieldUtils.readDeclaredField(publiclyShadowedChild, "d")); + + try { + FieldUtils.writeDeclaredField(privatelyShadowedChild, "s", "S"); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // pass + } + try { + FieldUtils.writeDeclaredField(privatelyShadowedChild, "b", Boolean.TRUE); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // pass + } + try { + FieldUtils.writeDeclaredField(privatelyShadowedChild, "i", Integer.valueOf(1)); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // pass + } + try { + FieldUtils.writeDeclaredField(privatelyShadowedChild, "d", Double.valueOf(1.0)); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // pass + } + } + + @Test + public void testWriteDeclaredNamedFieldForceAccess() throws Exception { + try { + FieldUtils.writeDeclaredField(publicChild, "s", "S", true); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // pass + } + try { + FieldUtils.writeDeclaredField(publicChild, "b", Boolean.TRUE, true); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // pass + } + try { + FieldUtils.writeDeclaredField(publicChild, "i", Integer.valueOf(1), true); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // pass + } + try { + FieldUtils.writeDeclaredField(publicChild, "d", Double.valueOf(1.0), true); + fail("Expected IllegalArgumentException"); + } catch (IllegalArgumentException e) { + // pass + } + + FieldUtils.writeDeclaredField(publiclyShadowedChild, "s", "S", true); + assertEquals("S", FieldUtils.readDeclaredField(publiclyShadowedChild, "s", true)); + FieldUtils.writeDeclaredField(publiclyShadowedChild, "b", Boolean.FALSE, true); + assertEquals(Boolean.FALSE, FieldUtils.readDeclaredField(publiclyShadowedChild, "b", true)); + FieldUtils.writeDeclaredField(publiclyShadowedChild, "i", Integer.valueOf(0), true); + assertEquals(Integer.valueOf(0), FieldUtils.readDeclaredField(publiclyShadowedChild, "i", true)); + FieldUtils.writeDeclaredField(publiclyShadowedChild, "d", Double.valueOf(0.0), true); + assertEquals(Double.valueOf(0.0), FieldUtils.readDeclaredField(publiclyShadowedChild, "d", true)); + + FieldUtils.writeDeclaredField(privatelyShadowedChild, "s", "S", true); + assertEquals("S", FieldUtils.readDeclaredField(privatelyShadowedChild, "s", true)); + FieldUtils.writeDeclaredField(privatelyShadowedChild, "b", Boolean.FALSE, true); + assertEquals(Boolean.FALSE, FieldUtils.readDeclaredField(privatelyShadowedChild, "b", true)); + FieldUtils.writeDeclaredField(privatelyShadowedChild, "i", Integer.valueOf(0), true); + assertEquals(Integer.valueOf(0), FieldUtils.readDeclaredField(privatelyShadowedChild, "i", true)); + FieldUtils.writeDeclaredField(privatelyShadowedChild, "d", Double.valueOf(0.0), true); + assertEquals(Double.valueOf(0.0), FieldUtils.readDeclaredField(privatelyShadowedChild, "d", true)); + } + + @Test(expected=IllegalArgumentException.class) + public void testAmbig() { + FieldUtils.getField(Ambig.class, "VALUE"); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/reflect/MemberUtils.java b/ApacheCommonsLang/org/apache/commons/lang3/reflect/MemberUtils.java new file mode 100644 index 0000000..a375f54 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/reflect/MemberUtils.java @@ -0,0 +1,185 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.reflect; + +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Member; +import java.lang.reflect.Modifier; + +import org.apache.commons.lang3.ClassUtils; + +/** + * Contains common code for working with Methods/Constructors, extracted and + * refactored from MethodUtils when it was imported from Commons + * BeanUtils. + * + * @since 2.5 + * @version $Id: MemberUtils.java 1143537 2011-07-06 19:30:22Z joehni $ + */ +abstract class MemberUtils { + // TODO extract an interface to implement compareParameterSets(...)? + + private static final int ACCESS_TEST = Modifier.PUBLIC | Modifier.PROTECTED | Modifier.PRIVATE; + + /** Array of primitive number types ordered by "promotability" */ + private static final Class[] ORDERED_PRIMITIVE_TYPES = { Byte.TYPE, Short.TYPE, + Character.TYPE, Integer.TYPE, Long.TYPE, Float.TYPE, Double.TYPE }; + + /** + * XXX Default access superclass workaround + * + * When a public class has a default access superclass with public members, + * these members are accessible. Calling them from compiled code works fine. + * Unfortunately, on some JVMs, using reflection to invoke these members + * seems to (wrongly) prevent access even when the modifier is public. + * Calling setAccessible(true) solves the problem but will only work from + * sufficiently privileged code. Better workarounds would be gratefully + * accepted. + * @param o the AccessibleObject to set as accessible + */ + static void setAccessibleWorkaround(AccessibleObject o) { + if (o == null || o.isAccessible()) { + return; + } + Member m = (Member) o; + if (Modifier.isPublic(m.getModifiers()) + && isPackageAccess(m.getDeclaringClass().getModifiers())) { + try { + o.setAccessible(true); + } catch (SecurityException e) { // NOPMD + // ignore in favor of subsequent IllegalAccessException + } + } + } + + /** + * Returns whether a given set of modifiers implies package access. + * @param modifiers to test + * @return true unless package/protected/private modifier detected + */ + static boolean isPackageAccess(int modifiers) { + return (modifiers & ACCESS_TEST) == 0; + } + + /** + * Returns whether a Member is accessible. + * @param m Member to check + * @return true if m is accessible + */ + static boolean isAccessible(Member m) { + return m != null && Modifier.isPublic(m.getModifiers()) && !m.isSynthetic(); + } + + /** + * Compares the relative fitness of two sets of parameter types in terms of + * matching a third set of runtime parameter types, such that a list ordered + * by the results of the comparison would return the best match first + * (least). + * + * @param left the "left" parameter set + * @param right the "right" parameter set + * @param actual the runtime parameter types to match against + * left/right + * @return int consistent with compare semantics + */ + static int compareParameterTypes(Class[] left, Class[] right, Class[] actual) { + float leftCost = getTotalTransformationCost(actual, left); + float rightCost = getTotalTransformationCost(actual, right); + return leftCost < rightCost ? -1 : rightCost < leftCost ? 1 : 0; + } + + /** + * Returns the sum of the object transformation cost for each class in the + * source argument list. + * @param srcArgs The source arguments + * @param destArgs The destination arguments + * @return The total transformation cost + */ + private static float getTotalTransformationCost(Class[] srcArgs, Class[] destArgs) { + float totalCost = 0.0f; + for (int i = 0; i < srcArgs.length; i++) { + Class srcClass, destClass; + srcClass = srcArgs[i]; + destClass = destArgs[i]; + totalCost += getObjectTransformationCost(srcClass, destClass); + } + return totalCost; + } + + /** + * Gets the number of steps required needed to turn the source class into + * the destination class. This represents the number of steps in the object + * hierarchy graph. + * @param srcClass The source class + * @param destClass The destination class + * @return The cost of transforming an object + */ + private static float getObjectTransformationCost(Class srcClass, Class destClass) { + if (destClass.isPrimitive()) { + return getPrimitivePromotionCost(srcClass, destClass); + } + float cost = 0.0f; + while (srcClass != null && !destClass.equals(srcClass)) { + if (destClass.isInterface() && ClassUtils.isAssignable(srcClass, destClass)) { + // slight penalty for interface match. + // we still want an exact match to override an interface match, + // but + // an interface match should override anything where we have to + // get a superclass. + cost += 0.25f; + break; + } + cost++; + srcClass = srcClass.getSuperclass(); + } + /* + * If the destination class is null, we've travelled all the way up to + * an Object match. We'll penalize this by adding 1.5 to the cost. + */ + if (srcClass == null) { + cost += 1.5f; + } + return cost; + } + + /** + * Gets the number of steps required to promote a primitive number to another + * type. + * @param srcClass the (primitive) source class + * @param destClass the (primitive) destination class + * @return The cost of promoting the primitive + */ + private static float getPrimitivePromotionCost(final Class srcClass, final Class destClass) { + float cost = 0.0f; + Class cls = srcClass; + if (!cls.isPrimitive()) { + // slight unwrapping penalty + cost += 0.1f; + cls = ClassUtils.wrapperToPrimitive(cls); + } + for (int i = 0; cls != destClass && i < ORDERED_PRIMITIVE_TYPES.length; i++) { + if (cls == ORDERED_PRIMITIVE_TYPES[i]) { + cost += 0.1f; + if (i < ORDERED_PRIMITIVE_TYPES.length - 1) { + cls = ORDERED_PRIMITIVE_TYPES[i + 1]; + } + } + } + return cost; + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/reflect/MethodUtils.java b/ApacheCommonsLang/org/apache/commons/lang3/reflect/MethodUtils.java new file mode 100644 index 0000000..2b1cf78 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/reflect/MethodUtils.java @@ -0,0 +1,537 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.reflect; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.ClassUtils; + +/** + *

Utility reflection methods focused on methods, originally from Commons BeanUtils. + * Differences from the BeanUtils version may be noted, especially where similar functionality + * already existed within Lang. + *

+ * + *

Known Limitations

+ *

Accessing Public Methods In A Default Access Superclass

+ *

There is an issue when invoking public methods contained in a default access superclass on JREs prior to 1.4. + * Reflection locates these methods fine and correctly assigns them as public. + * However, an IllegalAccessException is thrown if the method is invoked.

+ * + *

MethodUtils contains a workaround for this situation. + * It will attempt to call setAccessible on this method. + * If this call succeeds, then the method can be invoked as normal. + * This call will only succeed when the application has sufficient security privileges. + * If this call fails then the method may fail.

+ * + * @since 2.5 + * @version $Id: MethodUtils.java 1166253 2011-09-07 16:27:42Z ggregory $ + */ +public class MethodUtils { + + /** + *

MethodUtils instances should NOT be constructed in standard programming. + * Instead, the class should be used as + * MethodUtils.getAccessibleMethod(method).

+ * + *

This constructor is public to permit tools that require a JavaBean + * instance to operate.

+ */ + public MethodUtils() { + super(); + } + + /** + *

Invokes a named method whose parameter type matches the object type.

+ * + *

This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.

+ * + *

This method supports calls to methods taking primitive parameters + * via passing in wrapping classes. So, for example, a Boolean object + * would match a boolean primitive.

+ * + *

This is a convenient wrapper for + * {@link #invokeMethod(Object object,String methodName, Object[] args, Class[] parameterTypes)}. + *

+ * + * @param object invoke method on this object + * @param methodName get method with this name + * @param args use these arguments - treat null as empty array + * @return The value returned by the invoked method + * + * @throws NoSuchMethodException if there is no such accessible method + * @throws InvocationTargetException wraps an exception thrown by the method invoked + * @throws IllegalAccessException if the requested method is not accessible via reflection + */ + public static Object invokeMethod(Object object, String methodName, + Object... args) throws NoSuchMethodException, + IllegalAccessException, InvocationTargetException { + if (args == null) { + args = ArrayUtils.EMPTY_OBJECT_ARRAY; + } + int arguments = args.length; + Class[] parameterTypes = new Class[arguments]; + for (int i = 0; i < arguments; i++) { + parameterTypes[i] = args[i].getClass(); + } + return invokeMethod(object, methodName, args, parameterTypes); + } + + /** + *

Invokes a named method whose parameter type matches the object type.

+ * + *

This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.

+ * + *

This method supports calls to methods taking primitive parameters + * via passing in wrapping classes. So, for example, a Boolean object + * would match a boolean primitive.

+ * + * @param object invoke method on this object + * @param methodName get method with this name + * @param args use these arguments - treat null as empty array + * @param parameterTypes match these parameters - treat null as empty array + * @return The value returned by the invoked method + * + * @throws NoSuchMethodException if there is no such accessible method + * @throws InvocationTargetException wraps an exception thrown by the method invoked + * @throws IllegalAccessException if the requested method is not accessible via reflection + */ + public static Object invokeMethod(Object object, String methodName, + Object[] args, Class[] parameterTypes) + throws NoSuchMethodException, IllegalAccessException, + InvocationTargetException { + if (parameterTypes == null) { + parameterTypes = ArrayUtils.EMPTY_CLASS_ARRAY; + } + if (args == null) { + args = ArrayUtils.EMPTY_OBJECT_ARRAY; + } + Method method = getMatchingAccessibleMethod(object.getClass(), + methodName, parameterTypes); + if (method == null) { + throw new NoSuchMethodException("No such accessible method: " + + methodName + "() on object: " + + object.getClass().getName()); + } + return method.invoke(object, args); + } + + /** + *

Invokes a method whose parameter types match exactly the object + * types.

+ * + *

This uses reflection to invoke the method obtained from a call to + * getAccessibleMethod().

+ * + * @param object invoke method on this object + * @param methodName get method with this name + * @param args use these arguments - treat null as empty array + * @return The value returned by the invoked method + * + * @throws NoSuchMethodException if there is no such accessible method + * @throws InvocationTargetException wraps an exception thrown by the + * method invoked + * @throws IllegalAccessException if the requested method is not accessible + * via reflection + */ + public static Object invokeExactMethod(Object object, String methodName, + Object... args) throws NoSuchMethodException, + IllegalAccessException, InvocationTargetException { + if (args == null) { + args = ArrayUtils.EMPTY_OBJECT_ARRAY; + } + int arguments = args.length; + Class[] parameterTypes = new Class[arguments]; + for (int i = 0; i < arguments; i++) { + parameterTypes[i] = args[i].getClass(); + } + return invokeExactMethod(object, methodName, args, parameterTypes); + } + + /** + *

Invokes a method whose parameter types match exactly the parameter + * types given.

+ * + *

This uses reflection to invoke the method obtained from a call to + * getAccessibleMethod().

+ * + * @param object invoke method on this object + * @param methodName get method with this name + * @param args use these arguments - treat null as empty array + * @param parameterTypes match these parameters - treat null as empty array + * @return The value returned by the invoked method + * + * @throws NoSuchMethodException if there is no such accessible method + * @throws InvocationTargetException wraps an exception thrown by the + * method invoked + * @throws IllegalAccessException if the requested method is not accessible + * via reflection + */ + public static Object invokeExactMethod(Object object, String methodName, + Object[] args, Class[] parameterTypes) + throws NoSuchMethodException, IllegalAccessException, + InvocationTargetException { + if (args == null) { + args = ArrayUtils.EMPTY_OBJECT_ARRAY; + } + if (parameterTypes == null) { + parameterTypes = ArrayUtils.EMPTY_CLASS_ARRAY; + } + Method method = getAccessibleMethod(object.getClass(), methodName, + parameterTypes); + if (method == null) { + throw new NoSuchMethodException("No such accessible method: " + + methodName + "() on object: " + + object.getClass().getName()); + } + return method.invoke(object, args); + } + + /** + *

Invokes a static method whose parameter types match exactly the parameter + * types given.

+ * + *

This uses reflection to invoke the method obtained from a call to + * {@link #getAccessibleMethod(Class, String, Class[])}.

+ * + * @param cls invoke static method on this class + * @param methodName get method with this name + * @param args use these arguments - treat null as empty array + * @param parameterTypes match these parameters - treat null as empty array + * @return The value returned by the invoked method + * + * @throws NoSuchMethodException if there is no such accessible method + * @throws InvocationTargetException wraps an exception thrown by the + * method invoked + * @throws IllegalAccessException if the requested method is not accessible + * via reflection + */ + public static Object invokeExactStaticMethod(Class cls, String methodName, + Object[] args, Class[] parameterTypes) + throws NoSuchMethodException, IllegalAccessException, + InvocationTargetException { + if (args == null) { + args = ArrayUtils.EMPTY_OBJECT_ARRAY; + } + if (parameterTypes == null) { + parameterTypes = ArrayUtils.EMPTY_CLASS_ARRAY; + } + Method method = getAccessibleMethod(cls, methodName, parameterTypes); + if (method == null) { + throw new NoSuchMethodException("No such accessible method: " + + methodName + "() on class: " + cls.getName()); + } + return method.invoke(null, args); + } + + /** + *

Invokes a named static method whose parameter type matches the object type.

+ * + *

This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.

+ * + *

This method supports calls to methods taking primitive parameters + * via passing in wrapping classes. So, for example, a Boolean class + * would match a boolean primitive.

+ * + *

This is a convenient wrapper for + * {@link #invokeStaticMethod(Class objectClass,String methodName,Object [] args,Class[] parameterTypes)}. + *

+ * + * @param cls invoke static method on this class + * @param methodName get method with this name + * @param args use these arguments - treat null as empty array + * @return The value returned by the invoked method + * + * @throws NoSuchMethodException if there is no such accessible method + * @throws InvocationTargetException wraps an exception thrown by the + * method invoked + * @throws IllegalAccessException if the requested method is not accessible + * via reflection + */ + public static Object invokeStaticMethod(Class cls, String methodName, + Object... args) throws NoSuchMethodException, + IllegalAccessException, InvocationTargetException { + if (args == null) { + args = ArrayUtils.EMPTY_OBJECT_ARRAY; + } + int arguments = args.length; + Class[] parameterTypes = new Class[arguments]; + for (int i = 0; i < arguments; i++) { + parameterTypes[i] = args[i].getClass(); + } + return invokeStaticMethod(cls, methodName, args, parameterTypes); + } + + /** + *

Invokes a named static method whose parameter type matches the object type.

+ * + *

This method delegates the method search to {@link #getMatchingAccessibleMethod(Class, String, Class[])}.

+ * + *

This method supports calls to methods taking primitive parameters + * via passing in wrapping classes. So, for example, a Boolean class + * would match a boolean primitive.

+ * + * + * @param cls invoke static method on this class + * @param methodName get method with this name + * @param args use these arguments - treat null as empty array + * @param parameterTypes match these parameters - treat null as empty array + * @return The value returned by the invoked method + * + * @throws NoSuchMethodException if there is no such accessible method + * @throws InvocationTargetException wraps an exception thrown by the + * method invoked + * @throws IllegalAccessException if the requested method is not accessible + * via reflection + */ + public static Object invokeStaticMethod(Class cls, String methodName, + Object[] args, Class[] parameterTypes) + throws NoSuchMethodException, IllegalAccessException, + InvocationTargetException { + if (parameterTypes == null) { + parameterTypes = ArrayUtils.EMPTY_CLASS_ARRAY; + } + if (args == null) { + args = ArrayUtils.EMPTY_OBJECT_ARRAY; + } + Method method = getMatchingAccessibleMethod(cls, methodName, + parameterTypes); + if (method == null) { + throw new NoSuchMethodException("No such accessible method: " + + methodName + "() on class: " + cls.getName()); + } + return method.invoke(null, args); + } + + /** + *

Invokes a static method whose parameter types match exactly the object + * types.

+ * + *

This uses reflection to invoke the method obtained from a call to + * {@link #getAccessibleMethod(Class, String, Class[])}.

+ * + * @param cls invoke static method on this class + * @param methodName get method with this name + * @param args use these arguments - treat null as empty array + * @return The value returned by the invoked method + * + * @throws NoSuchMethodException if there is no such accessible method + * @throws InvocationTargetException wraps an exception thrown by the + * method invoked + * @throws IllegalAccessException if the requested method is not accessible + * via reflection + */ + public static Object invokeExactStaticMethod(Class cls, String methodName, + Object... args) throws NoSuchMethodException, + IllegalAccessException, InvocationTargetException { + if (args == null) { + args = ArrayUtils.EMPTY_OBJECT_ARRAY; + } + int arguments = args.length; + Class[] parameterTypes = new Class[arguments]; + for (int i = 0; i < arguments; i++) { + parameterTypes[i] = args[i].getClass(); + } + return invokeExactStaticMethod(cls, methodName, args, parameterTypes); + } + + /** + *

Returns an accessible method (that is, one that can be invoked via + * reflection) with given name and parameters. If no such method + * can be found, return null. + * This is just a convenient wrapper for + * {@link #getAccessibleMethod(Method method)}.

+ * + * @param cls get method from this class + * @param methodName get method with this name + * @param parameterTypes with these parameters types + * @return The accessible method + */ + public static Method getAccessibleMethod(Class cls, String methodName, + Class... parameterTypes) { + try { + return getAccessibleMethod(cls.getMethod(methodName, + parameterTypes)); + } catch (NoSuchMethodException e) { + return null; + } + } + + /** + *

Returns an accessible method (that is, one that can be invoked via + * reflection) that implements the specified Method. If no such method + * can be found, return null.

+ * + * @param method The method that we wish to call + * @return The accessible method + */ + public static Method getAccessibleMethod(Method method) { + if (!MemberUtils.isAccessible(method)) { + return null; + } + // If the declaring class is public, we are done + Class cls = method.getDeclaringClass(); + if (Modifier.isPublic(cls.getModifiers())) { + return method; + } + String methodName = method.getName(); + Class[] parameterTypes = method.getParameterTypes(); + + // Check the implemented interfaces and subinterfaces + method = getAccessibleMethodFromInterfaceNest(cls, methodName, + parameterTypes); + + // Check the superclass chain + if (method == null) { + method = getAccessibleMethodFromSuperclass(cls, methodName, + parameterTypes); + } + return method; + } + + /** + *

Returns an accessible method (that is, one that can be invoked via + * reflection) by scanning through the superclasses. If no such method + * can be found, return null.

+ * + * @param cls Class to be checked + * @param methodName Method name of the method we wish to call + * @param parameterTypes The parameter type signatures + * @return the accessible method or null if not found + */ + private static Method getAccessibleMethodFromSuperclass(Class cls, + String methodName, Class... parameterTypes) { + Class parentClass = cls.getSuperclass(); + while (parentClass != null) { + if (Modifier.isPublic(parentClass.getModifiers())) { + try { + return parentClass.getMethod(methodName, parameterTypes); + } catch (NoSuchMethodException e) { + return null; + } + } + parentClass = parentClass.getSuperclass(); + } + return null; + } + + /** + *

Returns an accessible method (that is, one that can be invoked via + * reflection) that implements the specified method, by scanning through + * all implemented interfaces and subinterfaces. If no such method + * can be found, return null.

+ * + *

There isn't any good reason why this method must be private. + * It is because there doesn't seem any reason why other classes should + * call this rather than the higher level methods.

+ * + * @param cls Parent class for the interfaces to be checked + * @param methodName Method name of the method we wish to call + * @param parameterTypes The parameter type signatures + * @return the accessible method or null if not found + */ + private static Method getAccessibleMethodFromInterfaceNest(Class cls, + String methodName, Class... parameterTypes) { + Method method = null; + + // Search up the superclass chain + for (; cls != null; cls = cls.getSuperclass()) { + + // Check the implemented interfaces of the parent class + Class[] interfaces = cls.getInterfaces(); + for (int i = 0; i < interfaces.length; i++) { + // Is this interface public? + if (!Modifier.isPublic(interfaces[i].getModifiers())) { + continue; + } + // Does the method exist on this interface? + try { + method = interfaces[i].getDeclaredMethod(methodName, + parameterTypes); + } catch (NoSuchMethodException e) { // NOPMD + /* + * Swallow, if no method is found after the loop then this + * method returns null. + */ + } + if (method != null) { + break; + } + // Recursively check our parent interfaces + method = getAccessibleMethodFromInterfaceNest(interfaces[i], + methodName, parameterTypes); + if (method != null) { + break; + } + } + } + return method; + } + + /** + *

Finds an accessible method that matches the given name and has compatible parameters. + * Compatible parameters mean that every method parameter is assignable from + * the given parameters. + * In other words, it finds a method with the given name + * that will take the parameters given.

+ * + *

This method is used by + * {@link + * #invokeMethod(Object object, String methodName, Object[] args, Class[] parameterTypes)}. + * + *

This method can match primitive parameter by passing in wrapper classes. + * For example, a Boolean will match a primitive boolean + * parameter. + * + * @param cls find method in this class + * @param methodName find method with this name + * @param parameterTypes find method with most compatible parameters + * @return The accessible method + */ + public static Method getMatchingAccessibleMethod(Class cls, + String methodName, Class... parameterTypes) { + try { + Method method = cls.getMethod(methodName, parameterTypes); + MemberUtils.setAccessibleWorkaround(method); + return method; + } catch (NoSuchMethodException e) { // NOPMD - Swallow the exception + } + // search through all methods + Method bestMatch = null; + Method[] methods = cls.getMethods(); + for (Method method : methods) { + // compare name and parameters + if (method.getName().equals(methodName) && ClassUtils.isAssignable(parameterTypes, method.getParameterTypes(), true)) { + // get accessible version of method + Method accessibleMethod = getAccessibleMethod(method); + if (accessibleMethod != null && (bestMatch == null || MemberUtils.compareParameterTypes( + accessibleMethod.getParameterTypes(), + bestMatch.getParameterTypes(), + parameterTypes) < 0)) { + bestMatch = accessibleMethod; + } + } + } + if (bestMatch != null) { + MemberUtils.setAccessibleWorkaround(bestMatch); + } + return bestMatch; + } +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/reflect/MethodUtilsTest.java b/ApacheCommonsLang/org/apache/commons/lang3/reflect/MethodUtilsTest.java new file mode 100644 index 0000000..552aa63 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/reflect/MethodUtilsTest.java @@ -0,0 +1,410 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.reflect; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNotSame; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.math.NumberUtils; +import org.apache.commons.lang3.mutable.Mutable; +import org.apache.commons.lang3.mutable.MutableObject; +import org.junit.Before; +import org.junit.Test; + +/** + * Unit tests MethodUtils + * @version $Id: MethodUtilsTest.java 1309977 2012-04-05 17:53:39Z ggregory $ + */ +public class MethodUtilsTest { + + private static interface PrivateInterface {} + + static class TestBeanWithInterfaces implements PrivateInterface { + public String foo() { + return "foo()"; + } + } + + public static class TestBean { + + public static String bar() { + return "bar()"; + } + + public static String bar(int i) { + return "bar(int)"; + } + + public static String bar(Integer i) { + return "bar(Integer)"; + } + + public static String bar(double d) { + return "bar(double)"; + } + + public static String bar(String s) { + return "bar(String)"; + } + + public static String bar(Object o) { + return "bar(Object)"; + } + + public static void oneParameterStatic(String s) { + // empty + } + + @SuppressWarnings("unused") + private void privateStuff() { + } + + + public String foo() { + return "foo()"; + } + + public String foo(int i) { + return "foo(int)"; + } + + public String foo(Integer i) { + return "foo(Integer)"; + } + + public String foo(double d) { + return "foo(double)"; + } + + public String foo(String s) { + return "foo(String)"; + } + + public String foo(Object o) { + return "foo(Object)"; + } + + public void oneParameter(String s) { + // empty + } + } + + private static class TestMutable implements Mutable { + @Override + public Object getValue() { + return null; + } + + @Override + public void setValue(Object value) { + } + } + + private TestBean testBean; + private Map, Class[]> classCache = new HashMap, Class[]>(); + + @Before + public void setUp() throws Exception { + testBean = new TestBean(); + classCache.clear(); + } + + @Test + public void testConstructor() throws Exception { + assertNotNull(MethodUtils.class.newInstance()); + } + + @Test + public void testInvokeMethod() throws Exception { + assertEquals("foo()", MethodUtils.invokeMethod(testBean, "foo", + (Object[]) ArrayUtils.EMPTY_CLASS_ARRAY)); + assertEquals("foo()", MethodUtils.invokeMethod(testBean, "foo", + (Object[]) null)); + assertEquals("foo()", MethodUtils.invokeMethod(testBean, "foo", + (Object[]) null, (Class[]) null)); + assertEquals("foo(String)", MethodUtils.invokeMethod(testBean, "foo", + "")); + assertEquals("foo(Object)", MethodUtils.invokeMethod(testBean, "foo", + new Object())); + assertEquals("foo(Object)", MethodUtils.invokeMethod(testBean, "foo", + Boolean.TRUE)); + assertEquals("foo(Integer)", MethodUtils.invokeMethod(testBean, "foo", + NumberUtils.INTEGER_ONE)); + assertEquals("foo(int)", MethodUtils.invokeMethod(testBean, "foo", + NumberUtils.BYTE_ONE)); + assertEquals("foo(double)", MethodUtils.invokeMethod(testBean, "foo", + NumberUtils.LONG_ONE)); + assertEquals("foo(double)", MethodUtils.invokeMethod(testBean, "foo", + NumberUtils.DOUBLE_ONE)); + } + + @Test + public void testInvokeExactMethod() throws Exception { + assertEquals("foo()", MethodUtils.invokeExactMethod(testBean, "foo", + (Object[]) ArrayUtils.EMPTY_CLASS_ARRAY)); + assertEquals("foo()", MethodUtils.invokeExactMethod(testBean, "foo", + (Object[]) null)); + assertEquals("foo()", MethodUtils.invokeExactMethod(testBean, "foo", + (Object[]) null, (Class[]) null)); + assertEquals("foo(String)", MethodUtils.invokeExactMethod(testBean, + "foo", "")); + assertEquals("foo(Object)", MethodUtils.invokeExactMethod(testBean, + "foo", new Object())); + assertEquals("foo(Integer)", MethodUtils.invokeExactMethod(testBean, + "foo", NumberUtils.INTEGER_ONE)); + assertEquals("foo(double)", MethodUtils.invokeExactMethod(testBean, + "foo", new Object[] { NumberUtils.DOUBLE_ONE }, + new Class[] { Double.TYPE })); + + try { + MethodUtils + .invokeExactMethod(testBean, "foo", NumberUtils.BYTE_ONE); + fail("should throw NoSuchMethodException"); + } catch (NoSuchMethodException e) { + } + try { + MethodUtils + .invokeExactMethod(testBean, "foo", NumberUtils.LONG_ONE); + fail("should throw NoSuchMethodException"); + } catch (NoSuchMethodException e) { + } + try { + MethodUtils.invokeExactMethod(testBean, "foo", Boolean.TRUE); + fail("should throw NoSuchMethodException"); + } catch (NoSuchMethodException e) { + } + } + + @Test + public void testInvokeStaticMethod() throws Exception { + assertEquals("bar()", MethodUtils.invokeStaticMethod(TestBean.class, + "bar", (Object[]) ArrayUtils.EMPTY_CLASS_ARRAY)); + assertEquals("bar()", MethodUtils.invokeStaticMethod(TestBean.class, + "bar", (Object[]) null)); + assertEquals("bar()", MethodUtils.invokeStaticMethod(TestBean.class, + "bar", (Object[]) null, (Class[]) null)); + assertEquals("bar(String)", MethodUtils.invokeStaticMethod( + TestBean.class, "bar", "")); + assertEquals("bar(Object)", MethodUtils.invokeStaticMethod( + TestBean.class, "bar", new Object())); + assertEquals("bar(Object)", MethodUtils.invokeStaticMethod( + TestBean.class, "bar", Boolean.TRUE)); + assertEquals("bar(Integer)", MethodUtils.invokeStaticMethod( + TestBean.class, "bar", NumberUtils.INTEGER_ONE)); + assertEquals("bar(int)", MethodUtils.invokeStaticMethod(TestBean.class, + "bar", NumberUtils.BYTE_ONE)); + assertEquals("bar(double)", MethodUtils.invokeStaticMethod( + TestBean.class, "bar", NumberUtils.LONG_ONE)); + assertEquals("bar(double)", MethodUtils.invokeStaticMethod( + TestBean.class, "bar", NumberUtils.DOUBLE_ONE)); + + try { + MethodUtils.invokeStaticMethod(TestBean.class, "does_not_exist"); + fail("should throw NoSuchMethodException"); + } catch (NoSuchMethodException e) { + } + } + + @Test + public void testInvokeExactStaticMethod() throws Exception { + assertEquals("bar()", MethodUtils.invokeExactStaticMethod(TestBean.class, + "bar", (Object[]) ArrayUtils.EMPTY_CLASS_ARRAY)); + assertEquals("bar()", MethodUtils.invokeExactStaticMethod(TestBean.class, + "bar", (Object[]) null)); + assertEquals("bar()", MethodUtils.invokeExactStaticMethod(TestBean.class, + "bar", (Object[]) null, (Class[]) null)); + assertEquals("bar(String)", MethodUtils.invokeExactStaticMethod( + TestBean.class, "bar", "")); + assertEquals("bar(Object)", MethodUtils.invokeExactStaticMethod( + TestBean.class, "bar", new Object())); + assertEquals("bar(Integer)", MethodUtils.invokeExactStaticMethod( + TestBean.class, "bar", NumberUtils.INTEGER_ONE)); + assertEquals("bar(double)", MethodUtils.invokeExactStaticMethod( + TestBean.class, "bar", new Object[] { NumberUtils.DOUBLE_ONE }, + new Class[] { Double.TYPE })); + + try { + MethodUtils.invokeExactStaticMethod(TestBean.class, "bar", + NumberUtils.BYTE_ONE); + fail("should throw NoSuchMethodException"); + } catch (NoSuchMethodException e) { + } + try { + MethodUtils.invokeExactStaticMethod(TestBean.class, "bar", + NumberUtils.LONG_ONE); + fail("should throw NoSuchMethodException"); + } catch (NoSuchMethodException e) { + } + try { + MethodUtils.invokeExactStaticMethod(TestBean.class, "bar", + Boolean.TRUE); + fail("should throw NoSuchMethodException"); + } catch (NoSuchMethodException e) { + } + } + + @Test + public void testGetAccessibleInterfaceMethod() throws Exception { + Class[][] p = { ArrayUtils.EMPTY_CLASS_ARRAY, null }; + for (Class[] element : p) { + Method method = TestMutable.class.getMethod("getValue", element); + Method accessibleMethod = MethodUtils.getAccessibleMethod(method); + assertNotSame(accessibleMethod, method); + assertSame(Mutable.class, accessibleMethod.getDeclaringClass()); + } + } + + @Test + public void testGetAccessibleMethodPrivateInterface() throws Exception { + Method expected = TestBeanWithInterfaces.class.getMethod("foo"); + assertNotNull(expected); + Method actual = MethodUtils.getAccessibleMethod(TestBeanWithInterfaces.class, "foo"); + assertNull(actual); + } + + @Test + public void testGetAccessibleInterfaceMethodFromDescription() + throws Exception { + Class[][] p = { ArrayUtils.EMPTY_CLASS_ARRAY, null }; + for (Class[] element : p) { + Method accessibleMethod = MethodUtils.getAccessibleMethod( + TestMutable.class, "getValue", element); + assertSame(Mutable.class, accessibleMethod.getDeclaringClass()); + } + } + + @Test + public void testGetAccessiblePublicMethod() throws Exception { + assertSame(MutableObject.class, MethodUtils.getAccessibleMethod( + MutableObject.class.getMethod("getValue", + ArrayUtils.EMPTY_CLASS_ARRAY)).getDeclaringClass()); + } + + @Test + public void testGetAccessiblePublicMethodFromDescription() throws Exception { + assertSame(MutableObject.class, MethodUtils.getAccessibleMethod( + MutableObject.class, "getValue", ArrayUtils.EMPTY_CLASS_ARRAY) + .getDeclaringClass()); + } + + @Test + public void testGetAccessibleMethodInaccessible() throws Exception { + Method expected = TestBean.class.getDeclaredMethod("privateStuff"); + Method actual = MethodUtils.getAccessibleMethod(expected); + assertNull(actual); + } + + @Test + public void testGetMatchingAccessibleMethod() throws Exception { + expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo", + ArrayUtils.EMPTY_CLASS_ARRAY, ArrayUtils.EMPTY_CLASS_ARRAY); + expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo", + null, ArrayUtils.EMPTY_CLASS_ARRAY); + expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo", + singletonArray(String.class), singletonArray(String.class)); + expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo", + singletonArray(Object.class), singletonArray(Object.class)); + expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo", + singletonArray(Boolean.class), singletonArray(Object.class)); + expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo", + singletonArray(Byte.class), singletonArray(Integer.TYPE)); + expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo", + singletonArray(Byte.TYPE), singletonArray(Integer.TYPE)); + expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo", + singletonArray(Short.class), singletonArray(Integer.TYPE)); + expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo", + singletonArray(Short.TYPE), singletonArray(Integer.TYPE)); + expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo", + singletonArray(Character.class), singletonArray(Integer.TYPE)); + expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo", + singletonArray(Character.TYPE), singletonArray(Integer.TYPE)); + expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo", + singletonArray(Integer.class), singletonArray(Integer.class)); + expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo", + singletonArray(Integer.TYPE), singletonArray(Integer.TYPE)); + expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo", + singletonArray(Long.class), singletonArray(Double.TYPE)); + expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo", + singletonArray(Long.TYPE), singletonArray(Double.TYPE)); + expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo", + singletonArray(Float.class), singletonArray(Double.TYPE)); + expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo", + singletonArray(Float.TYPE), singletonArray(Double.TYPE)); + expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo", + singletonArray(Double.class), singletonArray(Double.TYPE)); + expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo", + singletonArray(Double.TYPE), singletonArray(Double.TYPE)); + expectMatchingAccessibleMethodParameterTypes(TestBean.class, "foo", + singletonArray(Double.TYPE), singletonArray(Double.TYPE)); + expectMatchingAccessibleMethodParameterTypes(InheritanceBean.class, "testOne", + singletonArray(ParentObject.class), singletonArray(ParentObject.class)); + expectMatchingAccessibleMethodParameterTypes(InheritanceBean.class, "testOne", + singletonArray(ChildObject.class), singletonArray(ParentObject.class)); + expectMatchingAccessibleMethodParameterTypes(InheritanceBean.class, "testTwo", + singletonArray(ParentObject.class), singletonArray(GrandParentObject.class)); + expectMatchingAccessibleMethodParameterTypes(InheritanceBean.class, "testTwo", + singletonArray(ChildObject.class), singletonArray(ChildInterface.class)); + } + + private void expectMatchingAccessibleMethodParameterTypes(Class cls, + String methodName, Class[] requestTypes, Class[] actualTypes) { + Method m = MethodUtils.getMatchingAccessibleMethod(cls, methodName, + requestTypes); + assertTrue(toString(m.getParameterTypes()) + " not equals " + + toString(actualTypes), Arrays.equals(actualTypes, m + .getParameterTypes())); + } + + private String toString(Class[] c) { + return Arrays.asList(c).toString(); + } + + private Class[] singletonArray(Class c) { + Class[] result = classCache.get(c); + if (result == null) { + result = new Class[] { c }; + classCache.put(c, result); + } + return result; + } + + public static class InheritanceBean { + public void testOne(Object obj) {} + public void testOne(GrandParentObject obj) {} + public void testOne(ParentObject obj) {} + public void testTwo(Object obj) {} + public void testTwo(GrandParentObject obj) {} + public void testTwo(ChildInterface obj) {} + } + + interface ChildInterface {} + public static class GrandParentObject {} + public static class ParentObject extends GrandParentObject {} + public static class ChildObject extends ParentObject implements ChildInterface {} + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/reflect/TypeUtils.java b/ApacheCommonsLang/org/apache/commons/lang3/reflect/TypeUtils.java new file mode 100644 index 0000000..d25df20 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/reflect/TypeUtils.java @@ -0,0 +1,1101 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.reflect; + +import java.lang.reflect.Array; +import java.lang.reflect.GenericArrayType; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.lang.reflect.WildcardType; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.commons.lang3.ClassUtils; + +/** + *

Utility methods focusing on type inspection, particularly with regard to + * generics.

+ * + * @since 3.0 + * @version $Id: TypeUtils.java 1203429 2011-11-17 23:59:36Z mbenson $ + */ +public class TypeUtils { + + /** + *

TypeUtils instances should NOT be constructed in standard + * programming. Instead, the class should be used as + * TypeUtils.isAssignable(cls, toClass).

This + * constructor is public to permit tools that require a JavaBean instance to + * operate.

+ */ + public TypeUtils() { + super(); + } + + /** + *

Checks if the subject type may be implicitly cast to the target type + * following the Java generics rules. If both types are {@link Class} + * objects, the method returns the result of + * {@link ClassUtils#isAssignable(Class, Class)}.

+ * + * @param type the subject type to be assigned to the target type + * @param toType the target type + * @return true if type is assignable to toType. + */ + public static boolean isAssignable(Type type, Type toType) { + return isAssignable(type, toType, null); + } + + /** + *

Checks if the subject type may be implicitly cast to the target type + * following the Java generics rules.

+ * + * @param type the subject type to be assigned to the target type + * @param toType the target type + * @param typeVarAssigns optional map of type variable assignments + * @return true if type is assignable to toType. + */ + private static boolean isAssignable(Type type, Type toType, + Map, Type> typeVarAssigns) { + if (toType == null || toType instanceof Class) { + return isAssignable(type, (Class) toType); + } + + if (toType instanceof ParameterizedType) { + return isAssignable(type, (ParameterizedType) toType, typeVarAssigns); + } + + if (toType instanceof GenericArrayType) { + return isAssignable(type, (GenericArrayType) toType, typeVarAssigns); + } + + if (toType instanceof WildcardType) { + return isAssignable(type, (WildcardType) toType, typeVarAssigns); + } + + // * + if (toType instanceof TypeVariable) { + return isAssignable(type, (TypeVariable) toType, typeVarAssigns); + } + // */ + + throw new IllegalStateException("found an unhandled type: " + toType); + } + + /** + *

Checks if the subject type may be implicitly cast to the target class + * following the Java generics rules.

+ * + * @param type the subject type to be assigned to the target type + * @param toClass the target class + * @return true if type is assignable to toClass. + */ + private static boolean isAssignable(Type type, Class toClass) { + if (type == null) { + // consistency with ClassUtils.isAssignable() behavior + return toClass == null || !toClass.isPrimitive(); + } + + // only a null type can be assigned to null type which + // would have cause the previous to return true + if (toClass == null) { + return false; + } + + // all types are assignable to themselves + if (toClass.equals(type)) { + return true; + } + + if (type instanceof Class) { + // just comparing two classes + return ClassUtils.isAssignable((Class) type, toClass); + } + + if (type instanceof ParameterizedType) { + // only have to compare the raw type to the class + return isAssignable(getRawType((ParameterizedType) type), toClass); + } + + // * + if (type instanceof TypeVariable) { + // if any of the bounds are assignable to the class, then the + // type is assignable to the class. + for (Type bound : ((TypeVariable) type).getBounds()) { + if (isAssignable(bound, toClass)) { + return true; + } + } + + return false; + } + + // the only classes to which a generic array type can be assigned + // are class Object and array classes + if (type instanceof GenericArrayType) { + return toClass.equals(Object.class) + || toClass.isArray() + && isAssignable(((GenericArrayType) type).getGenericComponentType(), toClass + .getComponentType()); + } + + // wildcard types are not assignable to a class (though one would think + // "? super Object" would be assignable to Object) + if (type instanceof WildcardType) { + return false; + } + + throw new IllegalStateException("found an unhandled type: " + type); + } + + /** + *

Checks if the subject type may be implicitly cast to the target + * parameterized type following the Java generics rules.

+ * + * @param type the subject type to be assigned to the target type + * @param toParameterizedType the target parameterized type + * @param typeVarAssigns a map with type variables + * @return true if type is assignable to toType. + */ + private static boolean isAssignable(Type type, ParameterizedType toParameterizedType, + Map, Type> typeVarAssigns) { + if (type == null) { + return true; + } + + // only a null type can be assigned to null type which + // would have cause the previous to return true + if (toParameterizedType == null) { + return false; + } + + // all types are assignable to themselves + if (toParameterizedType.equals(type)) { + return true; + } + + // get the target type's raw type + Class toClass = getRawType(toParameterizedType); + // get the subject type's type arguments including owner type arguments + // and supertype arguments up to and including the target class. + Map, Type> fromTypeVarAssigns = getTypeArguments(type, toClass, null); + + // null means the two types are not compatible + if (fromTypeVarAssigns == null) { + return false; + } + + // compatible types, but there's no type arguments. this is equivalent + // to comparing Map< ?, ? > to Map, and raw types are always assignable + // to parameterized types. + if (fromTypeVarAssigns.isEmpty()) { + return true; + } + + // get the target type's type arguments including owner type arguments + Map, Type> toTypeVarAssigns = getTypeArguments(toParameterizedType, + toClass, typeVarAssigns); + + // now to check each type argument + for (TypeVariable var : toTypeVarAssigns.keySet()) { + Type toTypeArg = unrollVariableAssignments(var, toTypeVarAssigns); + Type fromTypeArg = unrollVariableAssignments(var, fromTypeVarAssigns); + + // parameters must either be absent from the subject type, within + // the bounds of the wildcard type, or be an exact match to the + // parameters of the target type. + if (fromTypeArg != null + && !toTypeArg.equals(fromTypeArg) + && !(toTypeArg instanceof WildcardType && isAssignable(fromTypeArg, toTypeArg, + typeVarAssigns))) { + return false; + } + } + + return true; + } + + private static Type unrollVariableAssignments(TypeVariable var, Map, Type> typeVarAssigns) { + Type result; + do { + result = typeVarAssigns.get(var); + if (result instanceof TypeVariable && !result.equals(var)) { + var = (TypeVariable) result; + continue; + } + break; + } while (true); + return result; + } + + /** + *

Checks if the subject type may be implicitly cast to the target + * generic array type following the Java generics rules.

+ * + * @param type the subject type to be assigned to the target type + * @param toGenericArrayType the target generic array type + * @param typeVarAssigns a map with type variables + * @return true if type is assignable to + * toGenericArrayType. + */ + private static boolean isAssignable(Type type, GenericArrayType toGenericArrayType, + Map, Type> typeVarAssigns) { + if (type == null) { + return true; + } + + // only a null type can be assigned to null type which + // would have cause the previous to return true + if (toGenericArrayType == null) { + return false; + } + + // all types are assignable to themselves + if (toGenericArrayType.equals(type)) { + return true; + } + + Type toComponentType = toGenericArrayType.getGenericComponentType(); + + if (type instanceof Class) { + Class cls = (Class) type; + + // compare the component types + return cls.isArray() + && isAssignable(cls.getComponentType(), toComponentType, typeVarAssigns); + } + + if (type instanceof GenericArrayType) { + // compare the component types + return isAssignable(((GenericArrayType) type).getGenericComponentType(), + toComponentType, typeVarAssigns); + } + + if (type instanceof WildcardType) { + // so long as one of the upper bounds is assignable, it's good + for (Type bound : getImplicitUpperBounds((WildcardType) type)) { + if (isAssignable(bound, toGenericArrayType)) { + return true; + } + } + + return false; + } + + if (type instanceof TypeVariable) { + // probably should remove the following logic and just return false. + // type variables cannot specify arrays as bounds. + for (Type bound : getImplicitBounds((TypeVariable) type)) { + if (isAssignable(bound, toGenericArrayType)) { + return true; + } + } + + return false; + } + + if (type instanceof ParameterizedType) { + // the raw type of a parameterized type is never an array or + // generic array, otherwise the declaration would look like this: + // Collection[]< ? extends String > collection; + return false; + } + + throw new IllegalStateException("found an unhandled type: " + type); + } + + /** + *

Checks if the subject type may be implicitly cast to the target + * wildcard type following the Java generics rules.

+ * + * @param type the subject type to be assigned to the target type + * @param toWildcardType the target wildcard type + * @param typeVarAssigns a map with type variables + * @return true if type is assignable to + * toWildcardType. + */ + private static boolean isAssignable(Type type, WildcardType toWildcardType, + Map, Type> typeVarAssigns) { + if (type == null) { + return true; + } + + // only a null type can be assigned to null type which + // would have cause the previous to return true + if (toWildcardType == null) { + return false; + } + + // all types are assignable to themselves + if (toWildcardType.equals(type)) { + return true; + } + + Type[] toUpperBounds = getImplicitUpperBounds(toWildcardType); + Type[] toLowerBounds = getImplicitLowerBounds(toWildcardType); + + if (type instanceof WildcardType) { + WildcardType wildcardType = (WildcardType) type; + Type[] upperBounds = getImplicitUpperBounds(wildcardType); + Type[] lowerBounds = getImplicitLowerBounds(wildcardType); + + for (Type toBound : toUpperBounds) { + // if there are assignments for unresolved type variables, + // now's the time to substitute them. + toBound = substituteTypeVariables(toBound, typeVarAssigns); + + // each upper bound of the subject type has to be assignable to + // each + // upper bound of the target type + for (Type bound : upperBounds) { + if (!isAssignable(bound, toBound, typeVarAssigns)) { + return false; + } + } + } + + for (Type toBound : toLowerBounds) { + // if there are assignments for unresolved type variables, + // now's the time to substitute them. + toBound = substituteTypeVariables(toBound, typeVarAssigns); + + // each lower bound of the target type has to be assignable to + // each + // lower bound of the subject type + for (Type bound : lowerBounds) { + if (!isAssignable(toBound, bound, typeVarAssigns)) { + return false; + } + } + } + + return true; + } + + for (Type toBound : toUpperBounds) { + // if there are assignments for unresolved type variables, + // now's the time to substitute them. + if (!isAssignable(type, substituteTypeVariables(toBound, typeVarAssigns), + typeVarAssigns)) { + return false; + } + } + + for (Type toBound : toLowerBounds) { + // if there are assignments for unresolved type variables, + // now's the time to substitute them. + if (!isAssignable(substituteTypeVariables(toBound, typeVarAssigns), type, + typeVarAssigns)) { + return false; + } + } + + return true; + } + + /** + *

Checks if the subject type may be implicitly cast to the target type + * variable following the Java generics rules.

+ * + * @param type the subject type to be assigned to the target type + * @param toTypeVariable the target type variable + * @param typeVarAssigns a map with type variables + * @return true if type is assignable to + * toTypeVariable. + */ + private static boolean isAssignable(Type type, TypeVariable toTypeVariable, + Map, Type> typeVarAssigns) { + if (type == null) { + return true; + } + + // only a null type can be assigned to null type which + // would have cause the previous to return true + if (toTypeVariable == null) { + return false; + } + + // all types are assignable to themselves + if (toTypeVariable.equals(type)) { + return true; + } + + if (type instanceof TypeVariable) { + // a type variable is assignable to another type variable, if + // and only if the former is the latter, extends the latter, or + // is otherwise a descendant of the latter. + Type[] bounds = getImplicitBounds((TypeVariable) type); + + for (Type bound : bounds) { + if (isAssignable(bound, toTypeVariable, typeVarAssigns)) { + return true; + } + } + } + + if (type instanceof Class || type instanceof ParameterizedType + || type instanceof GenericArrayType || type instanceof WildcardType) { + return false; + } + + throw new IllegalStateException("found an unhandled type: " + type); + } + + /** + *

+ * + * @param type the type to be replaced + * @param typeVarAssigns the map with type variables + * @return the replaced type + * @throws IllegalArgumentException if the type cannot be substituted + */ + private static Type substituteTypeVariables(Type type, Map, Type> typeVarAssigns) { + if (type instanceof TypeVariable && typeVarAssigns != null) { + Type replacementType = typeVarAssigns.get(type); + + if (replacementType == null) { + throw new IllegalArgumentException("missing assignment type for type variable " + + type); + } + + return replacementType; + } + + return type; + } + + /** + *

Retrieves all the type arguments for this parameterized type + * including owner hierarchy arguments such as + * Outer.Inner.DeepInner . The arguments are returned in a + * {@link Map} specifying the argument type for each {@link TypeVariable}. + *

+ * + * @param type specifies the subject parameterized type from which to + * harvest the parameters. + * @return a map of the type arguments to their respective type variables. + */ + public static Map, Type> getTypeArguments(ParameterizedType type) { + return getTypeArguments(type, getRawType(type), null); + } + + /** + *

Gets the type arguments of a class/interface based on a subtype. For + * instance, this method will determine that both of the parameters for the + * interface {@link Map} are {@link Object} for the subtype + * {@link java.util.Properties Properties} even though the subtype does not + * directly implement the Map interface.

This method + * returns null if type is not assignable to + * toClass. It returns an empty map if none of the classes or + * interfaces in its inheritance hierarchy specify any type arguments.

+ *

A side-effect of this method is that it also retrieves the type + * arguments for the classes and interfaces that are part of the hierarchy + * between type and toClass. So with the above + * example, this method will also determine that the type arguments for + * {@link java.util.Hashtable Hashtable} are also both Object. + * In cases where the interface specified by toClass is + * (indirectly) implemented more than once (e.g. where toClass + * specifies the interface {@link java.lang.Iterable Iterable} and + * type specifies a parameterized type that implements both + * {@link java.util.Set Set} and {@link java.util.Collection Collection}), + * this method will look at the inheritance hierarchy of only one of the + * implementations/subclasses; the first interface encountered that isn't a + * subinterface to one of the others in the type to + * toClass hierarchy.

+ * + * @param type the type from which to determine the type parameters of + * toClass + * @param toClass the class whose type parameters are to be determined based + * on the subtype type + * @return a map of the type assignments for the type variables in each type + * in the inheritance hierarchy from type to + * toClass inclusive. + */ + public static Map, Type> getTypeArguments(Type type, Class toClass) { + return getTypeArguments(type, toClass, null); + } + + /** + *

Return a map of the type arguments of type in the context of toClass.

+ * + * @param type the type in question + * @param toClass the class + * @param subtypeVarAssigns a map with type variables + * @return the map with type arguments + */ + private static Map, Type> getTypeArguments(Type type, Class toClass, + Map, Type> subtypeVarAssigns) { + if (type instanceof Class) { + return getTypeArguments((Class) type, toClass, subtypeVarAssigns); + } + + if (type instanceof ParameterizedType) { + return getTypeArguments((ParameterizedType) type, toClass, subtypeVarAssigns); + } + + if (type instanceof GenericArrayType) { + return getTypeArguments(((GenericArrayType) type).getGenericComponentType(), toClass + .isArray() ? toClass.getComponentType() : toClass, subtypeVarAssigns); + } + + // since wildcard types are not assignable to classes, should this just + // return null? + if (type instanceof WildcardType) { + for (Type bound : getImplicitUpperBounds((WildcardType) type)) { + // find the first bound that is assignable to the target class + if (isAssignable(bound, toClass)) { + return getTypeArguments(bound, toClass, subtypeVarAssigns); + } + } + + return null; + } + + // * + if (type instanceof TypeVariable) { + for (Type bound : getImplicitBounds((TypeVariable) type)) { + // find the first bound that is assignable to the target class + if (isAssignable(bound, toClass)) { + return getTypeArguments(bound, toClass, subtypeVarAssigns); + } + } + + return null; + } + // */ + + throw new IllegalStateException("found an unhandled type: " + type); + } + + /** + *

Return a map of the type arguments of a parameterized type in the context of toClass.

+ * + * @param parameterizedType the parameterized type + * @param toClass the class + * @param subtypeVarAssigns a map with type variables + * @return the map with type arguments + */ + private static Map, Type> getTypeArguments( + ParameterizedType parameterizedType, Class toClass, + Map, Type> subtypeVarAssigns) { + Class cls = getRawType(parameterizedType); + + // make sure they're assignable + if (!isAssignable(cls, toClass)) { + return null; + } + + Type ownerType = parameterizedType.getOwnerType(); + Map, Type> typeVarAssigns; + + if (ownerType instanceof ParameterizedType) { + // get the owner type arguments first + ParameterizedType parameterizedOwnerType = (ParameterizedType) ownerType; + typeVarAssigns = getTypeArguments(parameterizedOwnerType, + getRawType(parameterizedOwnerType), subtypeVarAssigns); + } else { + // no owner, prep the type variable assignments map + typeVarAssigns = subtypeVarAssigns == null ? new HashMap, Type>() + : new HashMap, Type>(subtypeVarAssigns); + } + + // get the subject parameterized type's arguments + Type[] typeArgs = parameterizedType.getActualTypeArguments(); + // and get the corresponding type variables from the raw class + TypeVariable[] typeParams = cls.getTypeParameters(); + + // map the arguments to their respective type variables + for (int i = 0; i < typeParams.length; i++) { + Type typeArg = typeArgs[i]; + typeVarAssigns.put(typeParams[i], typeVarAssigns.containsKey(typeArg) ? typeVarAssigns + .get(typeArg) : typeArg); + } + + if (toClass.equals(cls)) { + // target class has been reached. Done. + return typeVarAssigns; + } + + // walk the inheritance hierarchy until the target class is reached + return getTypeArguments(getClosestParentType(cls, toClass), toClass, typeVarAssigns); + } + + /** + *

Return a map of the type arguments of a class in the context of toClass.

+ * + * @param cls the class in question + * @param toClass the context class + * @param subtypeVarAssigns a map with type variables + * @return the map with type arguments + */ + private static Map, Type> getTypeArguments(Class cls, Class toClass, + Map, Type> subtypeVarAssigns) { + // make sure they're assignable + if (!isAssignable(cls, toClass)) { + return null; + } + + // can't work with primitives + if (cls.isPrimitive()) { + // both classes are primitives? + if (toClass.isPrimitive()) { + // dealing with widening here. No type arguments to be + // harvested with these two types. + return new HashMap, Type>(); + } + + // work with wrapper the wrapper class instead of the primitive + cls = ClassUtils.primitiveToWrapper(cls); + } + + // create a copy of the incoming map, or an empty one if it's null + HashMap, Type> typeVarAssigns = subtypeVarAssigns == null ? new HashMap, Type>() + : new HashMap, Type>(subtypeVarAssigns); + + // has target class been reached? + if (toClass.equals(cls)) { + return typeVarAssigns; + } + + // walk the inheritance hierarchy until the target class is reached + return getTypeArguments(getClosestParentType(cls, toClass), toClass, typeVarAssigns); + } + + /** + *

Tries to determine the type arguments of a class/interface based on a + * super parameterized type's type arguments. This method is the inverse of + * {@link #getTypeArguments(Type, Class)} which gets a class/interface's + * type arguments based on a subtype. It is far more limited in determining + * the type arguments for the subject class's type variables in that it can + * only determine those parameters that map from the subject {@link Class} + * object to the supertype.

Example: {@link java.util.TreeSet + * TreeSet} sets its parameter as the parameter for + * {@link java.util.NavigableSet NavigableSet}, which in turn sets the + * parameter of {@link java.util.SortedSet}, which in turn sets the + * parameter of {@link Set}, which in turn sets the parameter of + * {@link java.util.Collection}, which in turn sets the parameter of + * {@link java.lang.Iterable}. Since TreeSet's parameter maps + * (indirectly) to Iterable's parameter, it will be able to + * determine that based on the super type Iterable>>, the parameter of + * TreeSet is ? extends Map>.

+ * + * @param cls the class whose type parameters are to be determined + * @param superType the super type from which cls's type + * arguments are to be determined + * @return a map of the type assignments that could be determined for the + * type variables in each type in the inheritance hierarchy from + * type to toClass inclusive. + */ + public static Map, Type> determineTypeArguments(Class cls, + ParameterizedType superType) { + Class superClass = getRawType(superType); + + // compatibility check + if (!isAssignable(cls, superClass)) { + return null; + } + + if (cls.equals(superClass)) { + return getTypeArguments(superType, superClass, null); + } + + // get the next class in the inheritance hierarchy + Type midType = getClosestParentType(cls, superClass); + + // can only be a class or a parameterized type + if (midType instanceof Class) { + return determineTypeArguments((Class) midType, superType); + } + + ParameterizedType midParameterizedType = (ParameterizedType) midType; + Class midClass = getRawType(midParameterizedType); + // get the type variables of the mid class that map to the type + // arguments of the super class + Map, Type> typeVarAssigns = determineTypeArguments(midClass, superType); + // map the arguments of the mid type to the class type variables + mapTypeVariablesToArguments(cls, midParameterizedType, typeVarAssigns); + + return typeVarAssigns; + } + + /** + *

Performs a mapping of type variables.

+ * + * @param the generic type of the class in question + * @param cls the class in question + * @param parameterizedType the parameterized type + * @param typeVarAssigns the map to be filled + */ + private static void mapTypeVariablesToArguments(Class cls, + ParameterizedType parameterizedType, Map, Type> typeVarAssigns) { + // capture the type variables from the owner type that have assignments + Type ownerType = parameterizedType.getOwnerType(); + + if (ownerType instanceof ParameterizedType) { + // recursion to make sure the owner's owner type gets processed + mapTypeVariablesToArguments(cls, (ParameterizedType) ownerType, typeVarAssigns); + } + + // parameterizedType is a generic interface/class (or it's in the owner + // hierarchy of said interface/class) implemented/extended by the class + // cls. Find out which type variables of cls are type arguments of + // parameterizedType: + Type[] typeArgs = parameterizedType.getActualTypeArguments(); + + // of the cls's type variables that are arguments of parameterizedType, + // find out which ones can be determined from the super type's arguments + TypeVariable[] typeVars = getRawType(parameterizedType).getTypeParameters(); + + // use List view of type parameters of cls so the contains() method can be used: + List>> typeVarList = Arrays.asList(cls + .getTypeParameters()); + + for (int i = 0; i < typeArgs.length; i++) { + TypeVariable typeVar = typeVars[i]; + Type typeArg = typeArgs[i]; + + // argument of parameterizedType is a type variable of cls + if (typeVarList.contains(typeArg) + // type variable of parameterizedType has an assignment in + // the super type. + && typeVarAssigns.containsKey(typeVar)) { + // map the assignment to the cls's type variable + typeVarAssigns.put((TypeVariable) typeArg, typeVarAssigns.get(typeVar)); + } + } + } + + /** + *

Closest parent type? Closest to what? The closest parent type to the + * super class specified by superClass.

+ * + * @param cls the class in question + * @param superClass the super class + * @return the closes parent type + */ + private static Type getClosestParentType(Class cls, Class superClass) { + // only look at the interfaces if the super class is also an interface + if (superClass.isInterface()) { + // get the generic interfaces of the subject class + Type[] interfaceTypes = cls.getGenericInterfaces(); + // will hold the best generic interface match found + Type genericInterface = null; + + // find the interface closest to the super class + for (Type midType : interfaceTypes) { + Class midClass = null; + + if (midType instanceof ParameterizedType) { + midClass = getRawType((ParameterizedType) midType); + } else if (midType instanceof Class) { + midClass = (Class) midType; + } else { + throw new IllegalStateException("Unexpected generic" + + " interface type found: " + midType); + } + + // check if this interface is further up the inheritance chain + // than the previously found match + if (isAssignable(midClass, superClass) + && isAssignable(genericInterface, (Type) midClass)) { + genericInterface = midType; + } + } + + // found a match? + if (genericInterface != null) { + return genericInterface; + } + } + + // none of the interfaces were descendants of the target class, so the + // super class has to be one, instead + return cls.getGenericSuperclass(); + } + + /** + *

Checks if the given value can be assigned to the target type + * following the Java generics rules.

+ * + * @param value the value to be checked + * @param type the target type + * @return true of value is an instance of type. + */ + public static boolean isInstance(Object value, Type type) { + if (type == null) { + return false; + } + + return value == null ? !(type instanceof Class) || !((Class) type).isPrimitive() + : isAssignable(value.getClass(), type, null); + } + + /** + *

This method strips out the redundant upper bound types in type + * variable types and wildcard types (or it would with wildcard types if + * multiple upper bounds were allowed).

Example: with the variable + * type declaration: + * + *

 <K extends java.util.Collection<String> &
+     * java.util.List<String>> 
+ * + * since List is a subinterface of Collection, + * this method will return the bounds as if the declaration had been: + * + *
 <K extends java.util.List<String>> 
+ * + *

+ * + * @param bounds an array of types representing the upper bounds of either + * WildcardType or TypeVariable. + * @return an array containing the values from bounds minus the + * redundant types. + */ + public static Type[] normalizeUpperBounds(Type[] bounds) { + // don't bother if there's only one (or none) type + if (bounds.length < 2) { + return bounds; + } + + Set types = new HashSet(bounds.length); + + for (Type type1 : bounds) { + boolean subtypeFound = false; + + for (Type type2 : bounds) { + if (type1 != type2 && isAssignable(type2, type1, null)) { + subtypeFound = true; + break; + } + } + + if (!subtypeFound) { + types.add(type1); + } + } + + return types.toArray(new Type[types.size()]); + } + + /** + *

Returns an array containing the sole type of {@link Object} if + * {@link TypeVariable#getBounds()} returns an empty array. Otherwise, it + * returns the result of TypeVariable.getBounds() passed into + * {@link #normalizeUpperBounds}.

+ * + * @param typeVariable the subject type variable + * @return a non-empty array containing the bounds of the type variable. + */ + public static Type[] getImplicitBounds(TypeVariable typeVariable) { + Type[] bounds = typeVariable.getBounds(); + + return bounds.length == 0 ? new Type[] { Object.class } : normalizeUpperBounds(bounds); + } + + /** + *

Returns an array containing the sole value of {@link Object} if + * {@link WildcardType#getUpperBounds()} returns an empty array. Otherwise, + * it returns the result of WildcardType.getUpperBounds() + * passed into {@link #normalizeUpperBounds}.

+ * + * @param wildcardType the subject wildcard type + * @return a non-empty array containing the upper bounds of the wildcard + * type. + */ + public static Type[] getImplicitUpperBounds(WildcardType wildcardType) { + Type[] bounds = wildcardType.getUpperBounds(); + + return bounds.length == 0 ? new Type[] { Object.class } : normalizeUpperBounds(bounds); + } + + /** + *

Returns an array containing a single value of null if + * {@link WildcardType#getLowerBounds()} returns an empty array. Otherwise, + * it returns the result of WildcardType.getLowerBounds().

+ * + * @param wildcardType the subject wildcard type + * @return a non-empty array containing the lower bounds of the wildcard + * type. + */ + public static Type[] getImplicitLowerBounds(WildcardType wildcardType) { + Type[] bounds = wildcardType.getLowerBounds(); + + return bounds.length == 0 ? new Type[] { null } : bounds; + } + + /** + *

Determines whether or not specified types satisfy the bounds of their + * mapped type variables. When a type parameter extends another (such as + * ), uses another as a type parameter (such as + * ), or otherwise depends on + * another type variable to be specified, the dependencies must be included + * in typeVarAssigns.

+ * + * @param typeVarAssigns specifies the potential types to be assigned to the + * type variables. + * @return whether or not the types can be assigned to their respective type + * variables. + */ + public static boolean typesSatisfyVariables(Map, Type> typeVarAssigns) { + // all types must be assignable to all the bounds of the their mapped + // type variable. + for (Map.Entry, Type> entry : typeVarAssigns.entrySet()) { + TypeVariable typeVar = entry.getKey(); + Type type = entry.getValue(); + + for (Type bound : getImplicitBounds(typeVar)) { + if (!isAssignable(type, substituteTypeVariables(bound, typeVarAssigns), + typeVarAssigns)) { + return false; + } + } + } + + return true; + } + + /** + *

Transforms the passed in type to a {@code Class} object. Type-checking method of convenience.

+ * + * @param parameterizedType the type to be converted + * @return the corresponding {@code Class} object + * @throws IllegalStateException if the conversion fails + */ + private static Class getRawType(ParameterizedType parameterizedType) { + Type rawType = parameterizedType.getRawType(); + + // check if raw type is a Class object + // not currently necessary, but since the return type is Type instead of + // Class, there's enough reason to believe that future versions of Java + // may return other Type implementations. And type-safety checking is + // rarely a bad idea. + if (!(rawType instanceof Class)) { + throw new IllegalStateException("Wait... What!? Type of rawType: " + rawType); + } + + return (Class) rawType; + } + + /** + *

Get the raw type of a Java type, given its context. Primarily for use + * with {@link TypeVariable}s and {@link GenericArrayType}s, or when you do + * not know the runtime type of type: if you know you have a + * {@link Class} instance, it is already raw; if you know you have a + * {@link ParameterizedType}, its raw type is only a method call away.

+ * + * @param type to resolve + * @param assigningType type to be resolved against + * @return the resolved Class object or null if + * the type could not be resolved + */ + public static Class getRawType(Type type, Type assigningType) { + if (type instanceof Class) { + // it is raw, no problem + return (Class) type; + } + + if (type instanceof ParameterizedType) { + // simple enough to get the raw type of a ParameterizedType + return getRawType((ParameterizedType) type); + } + + if (type instanceof TypeVariable) { + if (assigningType == null) { + return null; + } + + // get the entity declaring this type variable + Object genericDeclaration = ((TypeVariable) type).getGenericDeclaration(); + + // can't get the raw type of a method- or constructor-declared type + // variable + if (!(genericDeclaration instanceof Class)) { + return null; + } + + // get the type arguments for the declaring class/interface based + // on the enclosing type + Map, Type> typeVarAssigns = getTypeArguments(assigningType, + (Class) genericDeclaration); + + // enclosingType has to be a subclass (or subinterface) of the + // declaring type + if (typeVarAssigns == null) { + return null; + } + + // get the argument assigned to this type variable + Type typeArgument = typeVarAssigns.get(type); + + if (typeArgument == null) { + return null; + } + + // get the argument for this type variable + return getRawType(typeArgument, assigningType); + } + + if (type instanceof GenericArrayType) { + // get raw component type + Class rawComponentType = getRawType(((GenericArrayType) type) + .getGenericComponentType(), assigningType); + + // create array type from raw component type and return its class + return Array.newInstance(rawComponentType, 0).getClass(); + } + + // (hand-waving) this is not the method you're looking for + if (type instanceof WildcardType) { + return null; + } + + throw new IllegalArgumentException("unknown type: " + type); + } + + /** + * Learn whether the specified type denotes an array type. + * @param type the type to be checked + * @return true if type is an array class or a {@link GenericArrayType}. + */ + public static boolean isArrayType(Type type) { + return type instanceof GenericArrayType || type instanceof Class && ((Class) type).isArray(); + } + + /** + * Get the array component type of type. + * @param type the type to be checked + * @return component type or null if type is not an array type + */ + public static Type getArrayComponentType(Type type) { + if (type instanceof Class) { + Class clazz = (Class) type; + return clazz.isArray() ? clazz.getComponentType() : null; + } + if (type instanceof GenericArrayType) { + return ((GenericArrayType) type).getGenericComponentType(); + } + return null; + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/reflect/TypeUtilsTest.java b/ApacheCommonsLang/org/apache/commons/lang3/reflect/TypeUtilsTest.java new file mode 100644 index 0000000..7d0e826 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/reflect/TypeUtilsTest.java @@ -0,0 +1,724 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.reflect; + +import java.io.Serializable; +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; +import java.net.URI; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.TreeSet; + +import org.apache.commons.lang3.StringEscapeUtils; +import org.apache.commons.lang3.reflect.testbed.Foo; +import org.apache.commons.lang3.reflect.testbed.GenericParent; +import org.apache.commons.lang3.reflect.testbed.GenericTypeHolder; +import org.apache.commons.lang3.reflect.testbed.StringParameterizedChild; +import org.junit.Assert; +import org.junit.Test; + +/** + * Test TypeUtils + * @version $Id: TypeUtilsTest.java 1203427 2011-11-17 23:58:31Z mbenson $ + */ +@SuppressWarnings({ "unchecked", "unused" , "rawtypes", "null"}) +//raw types, where used, are used purposely +public class TypeUtilsTest { + + public interface This { + } + + public class That implements This { + } + + public interface And extends This { + } + + public class The extends That implements And { + } + + public class Other implements This { + } + + public class Thing extends Other { + } + + public class Tester implements This { + } + + public This dis; + + public That dat; + + public The da; + + public Other uhder; + + public Thing ding; + + public TypeUtilsTest.Tester tester; + + public Tester tester2; + + public TypeUtilsTest.That dat2; + + public TypeUtilsTest.That dat3; + + public Comparable[] intWildcardComparable; + + public static Comparable stringComparable; + + public static Comparable uriComparable; + + public static Comparable intComparable; + + public static Comparable longComparable; + + public static URI uri; + + public void dummyMethod(List list0, List list1, List list2, + List list3, List list4, List list5, + List list6, List[] list7, List[] list8, List[] list9, + List[] list10, List[] list11, List[] list12, + List[] list13) { + } + + @SuppressWarnings("boxing") // deliberately used here + @Test + public void testIsAssignable() throws SecurityException, NoSuchMethodException, + NoSuchFieldException { + List list0 = null; + List list1 = null; + List list2 = null; + List list3 = null; + List list4 = null; + List list5 = null; + List list6 = null; + List[] list7 = null; + List[] list8 = null; + List[] list9 = null; + List[] list10 = null; + List[] list11 = null; + List[] list12 = null; + List[] list13; + Class clazz = getClass(); + Method method = clazz.getMethod("dummyMethod", List.class, List.class, List.class, + List.class, List.class, List.class, List.class, List[].class, List[].class, + List[].class, List[].class, List[].class, List[].class, List[].class); + Type[] types = method.getGenericParameterTypes(); +// list0 = list0; + delegateBooleanAssertion(types, 0, 0, true); + list1 = list0; + delegateBooleanAssertion(types, 0, 1, true); + list0 = list1; + delegateBooleanAssertion(types, 1, 0, true); + list2 = list0; + delegateBooleanAssertion(types, 0, 2, true); + list0 = list2; + delegateBooleanAssertion(types, 2, 0, true); + list3 = list0; + delegateBooleanAssertion(types, 0, 3, true); + list0 = list3; + delegateBooleanAssertion(types, 3, 0, true); + list4 = list0; + delegateBooleanAssertion(types, 0, 4, true); + list0 = list4; + delegateBooleanAssertion(types, 4, 0, true); + list5 = list0; + delegateBooleanAssertion(types, 0, 5, true); + list0 = list5; + delegateBooleanAssertion(types, 5, 0, true); + list6 = list0; + delegateBooleanAssertion(types, 0, 6, true); + list0 = list6; + delegateBooleanAssertion(types, 6, 0, true); +// list1 = list1; + delegateBooleanAssertion(types, 1, 1, true); + list2 = list1; + delegateBooleanAssertion(types, 1, 2, true); + list1 = (List) list2; + delegateBooleanAssertion(types, 2, 1, false); + list3 = list1; + delegateBooleanAssertion(types, 1, 3, true); + list1 = (List) list3; + delegateBooleanAssertion(types, 3, 1, false); + // list4 = list1; + delegateBooleanAssertion(types, 1, 4, false); + // list1 = list4; + delegateBooleanAssertion(types, 4, 1, false); + // list5 = list1; + delegateBooleanAssertion(types, 1, 5, false); + // list1 = list5; + delegateBooleanAssertion(types, 5, 1, false); + list6 = list1; + delegateBooleanAssertion(types, 1, 6, true); + list1 = (List) list6; + delegateBooleanAssertion(types, 6, 1, false); +// list2 = list2; + delegateBooleanAssertion(types, 2, 2, true); + list2 = list3; + delegateBooleanAssertion(types, 2, 3, false); + list2 = list4; + delegateBooleanAssertion(types, 3, 2, true); + list3 = (List) list2; + delegateBooleanAssertion(types, 2, 4, false); + list2 = list5; + delegateBooleanAssertion(types, 4, 2, true); + list4 = (List) list2; + delegateBooleanAssertion(types, 2, 5, false); + list2 = list6; + delegateBooleanAssertion(types, 5, 2, true); + list5 = (List) list2; + delegateBooleanAssertion(types, 2, 6, false); +// list3 = list3; + delegateBooleanAssertion(types, 6, 2, true); + list6 = (List) list2; + delegateBooleanAssertion(types, 3, 3, true); + // list4 = list3; + delegateBooleanAssertion(types, 3, 4, false); + // list3 = list4; + delegateBooleanAssertion(types, 4, 3, false); + // list5 = list3; + delegateBooleanAssertion(types, 3, 5, false); + // list3 = list5; + delegateBooleanAssertion(types, 5, 3, false); + list6 = list3; + delegateBooleanAssertion(types, 3, 6, true); + list3 = (List) list6; + delegateBooleanAssertion(types, 6, 3, false); +// list4 = list4; + delegateBooleanAssertion(types, 4, 4, true); + list5 = list4; + delegateBooleanAssertion(types, 4, 5, true); + list4 = (List) list5; + delegateBooleanAssertion(types, 5, 4, false); + list6 = list4; + delegateBooleanAssertion(types, 4, 6, true); + list4 = (List) list6; + delegateBooleanAssertion(types, 6, 4, false); +// list5 = list5; + delegateBooleanAssertion(types, 5, 5, true); + list6 = (List) list5; + delegateBooleanAssertion(types, 5, 6, false); + list5 = (List) list6; + delegateBooleanAssertion(types, 6, 5, false); +// list6 = list6; + delegateBooleanAssertion(types, 6, 6, true); + +// list7 = list7; + delegateBooleanAssertion(types, 7, 7, true); + list8 = list7; + delegateBooleanAssertion(types, 7, 8, true); + list7 = list8; + delegateBooleanAssertion(types, 8, 7, true); + list9 = list7; + delegateBooleanAssertion(types, 7, 9, true); + list7 = list9; + delegateBooleanAssertion(types, 9, 7, true); + list10 = list7; + delegateBooleanAssertion(types, 7, 10, true); + list7 = list10; + delegateBooleanAssertion(types, 10, 7, true); + list11 = list7; + delegateBooleanAssertion(types, 7, 11, true); + list7 = list11; + delegateBooleanAssertion(types, 11, 7, true); + list12 = list7; + delegateBooleanAssertion(types, 7, 12, true); + list7 = list12; + delegateBooleanAssertion(types, 12, 7, true); + list13 = list7; + delegateBooleanAssertion(types, 7, 13, true); + list7 = list13; + delegateBooleanAssertion(types, 13, 7, true); +// list8 = list8; + delegateBooleanAssertion(types, 8, 8, true); + list9 = list8; + delegateBooleanAssertion(types, 8, 9, true); + list8 = (List[]) list9; + delegateBooleanAssertion(types, 9, 8, false); + list10 = list8; + delegateBooleanAssertion(types, 8, 10, true); + list8 = (List[]) list10; // NOTE cast is required by Sun Java, but not by Eclipse + delegateBooleanAssertion(types, 10, 8, false); + // list11 = list8; + delegateBooleanAssertion(types, 8, 11, false); + // list8 = list11; + delegateBooleanAssertion(types, 11, 8, false); + // list12 = list8; + delegateBooleanAssertion(types, 8, 12, false); + // list8 = list12; + delegateBooleanAssertion(types, 12, 8, false); + list13 = list8; + delegateBooleanAssertion(types, 8, 13, true); + list8 = (List[]) list13; + delegateBooleanAssertion(types, 13, 8, false); +// list9 = list9; + delegateBooleanAssertion(types, 9, 9, true); + list10 = (List[]) list9; + delegateBooleanAssertion(types, 9, 10, false); + list9 = list10; + delegateBooleanAssertion(types, 10, 9, true); + list11 = (List[]) list9; + delegateBooleanAssertion(types, 9, 11, false); + list9 = list11; + delegateBooleanAssertion(types, 11, 9, true); + list12 = (List[]) list9; + delegateBooleanAssertion(types, 9, 12, false); + list9 = list12; + delegateBooleanAssertion(types, 12, 9, true); + list13 = (List[]) list9; + delegateBooleanAssertion(types, 9, 13, false); + list9 = list13; + delegateBooleanAssertion(types, 13, 9, true); +// list10 = list10; + delegateBooleanAssertion(types, 10, 10, true); + // list11 = list10; + delegateBooleanAssertion(types, 10, 11, false); + // list10 = list11; + delegateBooleanAssertion(types, 11, 10, false); + // list12 = list10; + delegateBooleanAssertion(types, 10, 12, false); + // list10 = list12; + delegateBooleanAssertion(types, 12, 10, false); + list13 = list10; + delegateBooleanAssertion(types, 10, 13, true); + list10 = (List[]) list13; + delegateBooleanAssertion(types, 13, 10, false); +// list11 = list11; + delegateBooleanAssertion(types, 11, 11, true); + list12 = list11; + delegateBooleanAssertion(types, 11, 12, true); + list11 = (List[]) list12; + delegateBooleanAssertion(types, 12, 11, false); + list13 = list11; + delegateBooleanAssertion(types, 11, 13, true); + list11 = (List[]) list13; + delegateBooleanAssertion(types, 13, 11, false); +// list12 = list12; + delegateBooleanAssertion(types, 12, 12, true); + list13 = (List[]) list12; + delegateBooleanAssertion(types, 12, 13, false); + list12 = (List[]) list13; + delegateBooleanAssertion(types, 13, 12, false); +// list13 = list13; + delegateBooleanAssertion(types, 13, 13, true); + Type disType = getClass().getField("dis").getGenericType(); + // Reporter.log( ( ( ParameterizedType ) disType + // ).getOwnerType().getClass().toString() ); + Type datType = getClass().getField("dat").getGenericType(); + Type daType = getClass().getField("da").getGenericType(); + Type uhderType = getClass().getField("uhder").getGenericType(); + Type dingType = getClass().getField("ding").getGenericType(); + Type testerType = getClass().getField("tester").getGenericType(); + Type tester2Type = getClass().getField("tester2").getGenericType(); + Type dat2Type = getClass().getField("dat2").getGenericType(); + Type dat3Type = getClass().getField("dat3").getGenericType(); + dis = dat; + Assert.assertTrue(TypeUtils.isAssignable(datType, disType)); + // dis = da; + Assert.assertFalse(TypeUtils.isAssignable(daType, disType)); + dis = uhder; + Assert.assertTrue(TypeUtils.isAssignable(uhderType, disType)); + dis = ding; + Assert.assertFalse(String.format("type %s not assignable to %s!", dingType, disType), + TypeUtils.isAssignable(dingType, disType)); + dis = tester; + Assert.assertTrue(TypeUtils.isAssignable(testerType, disType)); + // dis = tester2; + Assert.assertFalse(TypeUtils.isAssignable(tester2Type, disType)); + // dat = dat2; + Assert.assertFalse(TypeUtils.isAssignable(dat2Type, datType)); + // dat2 = dat; + Assert.assertFalse(TypeUtils.isAssignable(datType, dat2Type)); + // dat = dat3; + Assert.assertFalse(TypeUtils.isAssignable(dat3Type, datType)); + char ch = 0; + boolean bo = false; + byte by = 0; + short sh = 0; + int in = 0; + long lo = 0; + float fl = 0; + double du = 0; + du = ch; + Assert.assertTrue(TypeUtils.isAssignable(char.class, double.class)); + du = by; + Assert.assertTrue(TypeUtils.isAssignable(byte.class, double.class)); + du = sh; + Assert.assertTrue(TypeUtils.isAssignable(short.class, double.class)); + du = in; + Assert.assertTrue(TypeUtils.isAssignable(int.class, double.class)); + du = lo; + Assert.assertTrue(TypeUtils.isAssignable(long.class, double.class)); + du = fl; + Assert.assertTrue(TypeUtils.isAssignable(float.class, double.class)); + lo = in; + Assert.assertTrue(TypeUtils.isAssignable(int.class, long.class)); + lo = Integer.valueOf(0); + Assert.assertTrue(TypeUtils.isAssignable(Integer.class, long.class)); + // Long lngW = 1; + Assert.assertFalse(TypeUtils.isAssignable(int.class, Long.class)); + // lngW = Integer.valueOf( 0 ); + Assert.assertFalse(TypeUtils.isAssignable(Integer.class, Long.class)); + in = Integer.valueOf(0); + Assert.assertTrue(TypeUtils.isAssignable(Integer.class, int.class)); + Integer inte = in; + Assert.assertTrue(TypeUtils.isAssignable(int.class, Integer.class)); + Assert.assertTrue(TypeUtils.isAssignable(int.class, Number.class)); + Assert.assertTrue(TypeUtils.isAssignable(int.class, Object.class)); + Type intComparableType = getClass().getField("intComparable").getGenericType(); + intComparable = 1; + Assert.assertTrue(TypeUtils.isAssignable(int.class, intComparableType)); + Assert.assertTrue(TypeUtils.isAssignable(int.class, Comparable.class)); + Serializable ser = 1; + Assert.assertTrue(TypeUtils.isAssignable(int.class, Serializable.class)); + Type longComparableType = getClass().getField("longComparable").getGenericType(); + // longComparable = 1; + Assert.assertFalse(TypeUtils.isAssignable(int.class, longComparableType)); + // longComparable = Integer.valueOf( 0 ); + Assert.assertFalse(TypeUtils.isAssignable(Integer.class, longComparableType)); + // int[] ia; + // long[] la = ia; + Assert.assertFalse(TypeUtils.isAssignable(int[].class, long[].class)); + Integer[] ia = null; + Type caType = getClass().getField("intWildcardComparable").getGenericType(); + intWildcardComparable = ia; + Assert.assertTrue(TypeUtils.isAssignable(Integer[].class, caType)); + // int[] ina = ia; + Assert.assertFalse(TypeUtils.isAssignable(Integer[].class, int[].class)); + int[] ina = null; + Object[] oa; + // oa = ina; + Assert.assertFalse(TypeUtils.isAssignable(int[].class, Object[].class)); + oa = new Integer[0]; + Assert.assertTrue(TypeUtils.isAssignable(Integer[].class, Object[].class)); + Type bClassType = AClass.class.getField("bClass").getGenericType(); + Type cClassType = AClass.class.getField("cClass").getGenericType(); + Type dClassType = AClass.class.getField("dClass").getGenericType(); + Type eClassType = AClass.class.getField("eClass").getGenericType(); + Type fClassType = AClass.class.getField("fClass").getGenericType(); + AClass aClass = new AClass(new AAClass()); + aClass.bClass = aClass.cClass; + Assert.assertTrue(TypeUtils.isAssignable(cClassType, bClassType)); + aClass.bClass = aClass.dClass; + Assert.assertTrue(TypeUtils.isAssignable(dClassType, bClassType)); + aClass.bClass = aClass.eClass; + Assert.assertTrue(TypeUtils.isAssignable(eClassType, bClassType)); + aClass.bClass = aClass.fClass; + Assert.assertTrue(TypeUtils.isAssignable(fClassType, bClassType)); + aClass.cClass = aClass.dClass; + Assert.assertTrue(TypeUtils.isAssignable(dClassType, cClassType)); + aClass.cClass = aClass.eClass; + Assert.assertTrue(TypeUtils.isAssignable(eClassType, cClassType)); + aClass.cClass = aClass.fClass; + Assert.assertTrue(TypeUtils.isAssignable(fClassType, cClassType)); + aClass.dClass = aClass.eClass; + Assert.assertTrue(TypeUtils.isAssignable(eClassType, dClassType)); + aClass.dClass = aClass.fClass; + Assert.assertTrue(TypeUtils.isAssignable(fClassType, dClassType)); + aClass.eClass = aClass.fClass; + Assert.assertTrue(TypeUtils.isAssignable(fClassType, eClassType)); + } + + public void delegateBooleanAssertion(Type[] types, int i2, int i1, boolean expected) { + Type type1 = types[i1]; + Type type2 = types[i2]; + boolean isAssignable = TypeUtils.isAssignable(type2, type1); + + if (expected) { + Assert.assertTrue("[" + i1 + ", " + i2 + "]: From " + + StringEscapeUtils.escapeHtml4(String.valueOf(type2)) + " to " + + StringEscapeUtils.escapeHtml4(String.valueOf(type1)), isAssignable); + } else { + Assert.assertFalse("[" + i1 + ", " + i2 + "]: From " + + StringEscapeUtils.escapeHtml4(String.valueOf(type2)) + " to " + + StringEscapeUtils.escapeHtml4(String.valueOf(type1)), isAssignable); + } + } + + @SuppressWarnings("boxing") // boxing is deliberate here + @Test + public void testIsInstance() throws SecurityException, NoSuchFieldException { + Type intComparableType = getClass().getField("intComparable").getGenericType(); + Type uriComparableType = getClass().getField("uriComparable").getGenericType(); + intComparable = 1; + Assert.assertTrue(TypeUtils.isInstance(1, intComparableType)); + // uriComparable = 1; + Assert.assertFalse(TypeUtils.isInstance(1, uriComparableType)); + } + + @Test + public void testGetTypeArguments() { + Map, Type> typeVarAssigns; + TypeVariable treeSetTypeVar; + Type typeArg; + + typeVarAssigns = TypeUtils.getTypeArguments(Integer.class, Comparable.class); + treeSetTypeVar = Comparable.class.getTypeParameters()[0]; + Assert.assertTrue("Type var assigns for Comparable from Integer: " + typeVarAssigns, + typeVarAssigns.containsKey(treeSetTypeVar)); + typeArg = typeVarAssigns.get(treeSetTypeVar); + Assert.assertEquals("Type argument of Comparable from Integer: " + typeArg, Integer.class, + typeVarAssigns.get(treeSetTypeVar)); + + typeVarAssigns = TypeUtils.getTypeArguments(int.class, Comparable.class); + treeSetTypeVar = Comparable.class.getTypeParameters()[0]; + Assert.assertTrue("Type var assigns for Comparable from int: " + typeVarAssigns, + typeVarAssigns.containsKey(treeSetTypeVar)); + typeArg = typeVarAssigns.get(treeSetTypeVar); + Assert.assertEquals("Type argument of Comparable from int: " + typeArg, Integer.class, + typeVarAssigns.get(treeSetTypeVar)); + + Collection col = Arrays.asList(new Integer[0]); + typeVarAssigns = TypeUtils.getTypeArguments(List.class, Collection.class); + treeSetTypeVar = Comparable.class.getTypeParameters()[0]; + Assert.assertFalse("Type var assigns for Collection from List: " + typeVarAssigns, + typeVarAssigns.containsKey(treeSetTypeVar)); + + typeVarAssigns = TypeUtils.getTypeArguments(AAAClass.BBBClass.class, AAClass.BBClass.class); + Assert.assertTrue(typeVarAssigns.size() == 2); + Assert.assertEquals(String.class, typeVarAssigns.get(AAClass.class.getTypeParameters()[0])); + Assert.assertEquals(String.class, typeVarAssigns.get(AAClass.BBClass.class.getTypeParameters()[0])); + + typeVarAssigns = TypeUtils.getTypeArguments(Other.class, This.class); + Assert.assertEquals(2, typeVarAssigns.size()); + Assert.assertEquals(String.class, typeVarAssigns.get(This.class.getTypeParameters()[0])); + Assert.assertEquals(Other.class.getTypeParameters()[0], typeVarAssigns.get(This.class.getTypeParameters()[1])); + } + + @Test + public void testTypesSatisfyVariables() throws SecurityException, NoSuchFieldException, + NoSuchMethodException { + Map, Type> typeVarAssigns = new HashMap, Type>(); + Integer max = TypeUtilsTest.stub(); + typeVarAssigns.put(getClass().getMethod("stub").getTypeParameters()[0], Integer.class); + Assert.assertTrue(TypeUtils.typesSatisfyVariables(typeVarAssigns)); + typeVarAssigns.clear(); + typeVarAssigns.put(getClass().getMethod("stub2").getTypeParameters()[0], Integer.class); + Assert.assertTrue(TypeUtils.typesSatisfyVariables(typeVarAssigns)); + typeVarAssigns.clear(); + typeVarAssigns.put(getClass().getMethod("stub3").getTypeParameters()[0], Integer.class); + Assert.assertTrue(TypeUtils.typesSatisfyVariables(typeVarAssigns)); + } + + @Test + public void testDetermineTypeVariableAssignments() throws SecurityException, + NoSuchFieldException, NoSuchMethodException { + ParameterizedType iterableType = (ParameterizedType) getClass().getField("iterable") + .getGenericType(); + Map, Type> typeVarAssigns = TypeUtils.determineTypeArguments(TreeSet.class, + iterableType); + TypeVariable treeSetTypeVar = TreeSet.class.getTypeParameters()[0]; + Assert.assertTrue(typeVarAssigns.containsKey(treeSetTypeVar)); + Assert.assertEquals(iterableType.getActualTypeArguments()[0], typeVarAssigns + .get(treeSetTypeVar)); + } + + @Test + public void testGetRawType() throws SecurityException, NoSuchFieldException { + Type stringParentFieldType = GenericTypeHolder.class.getDeclaredField("stringParent") + .getGenericType(); + Type integerParentFieldType = GenericTypeHolder.class.getDeclaredField("integerParent") + .getGenericType(); + Type foosFieldType = GenericTypeHolder.class.getDeclaredField("foos").getGenericType(); + Type genericParentT = GenericParent.class.getTypeParameters()[0]; + Assert.assertEquals(GenericParent.class, TypeUtils.getRawType(stringParentFieldType, null)); + Assert + .assertEquals(GenericParent.class, TypeUtils.getRawType(integerParentFieldType, + null)); + Assert.assertEquals(List.class, TypeUtils.getRawType(foosFieldType, null)); + Assert.assertEquals(String.class, TypeUtils.getRawType(genericParentT, + StringParameterizedChild.class)); + Assert.assertEquals(String.class, TypeUtils.getRawType(genericParentT, + stringParentFieldType)); + Assert.assertEquals(Foo.class, TypeUtils.getRawType(Iterable.class.getTypeParameters()[0], + foosFieldType)); + Assert.assertEquals(Foo.class, TypeUtils.getRawType(List.class.getTypeParameters()[0], + foosFieldType)); + Assert.assertNull(TypeUtils.getRawType(genericParentT, GenericParent.class)); + Assert.assertEquals(GenericParent[].class, TypeUtils.getRawType(GenericTypeHolder.class + .getDeclaredField("barParents").getGenericType(), null)); + } + + @Test + public void testIsArrayTypeClasses() { + Assert.assertTrue(TypeUtils.isArrayType(boolean[].class)); + Assert.assertTrue(TypeUtils.isArrayType(byte[].class)); + Assert.assertTrue(TypeUtils.isArrayType(short[].class)); + Assert.assertTrue(TypeUtils.isArrayType(int[].class)); + Assert.assertTrue(TypeUtils.isArrayType(char[].class)); + Assert.assertTrue(TypeUtils.isArrayType(long[].class)); + Assert.assertTrue(TypeUtils.isArrayType(float[].class)); + Assert.assertTrue(TypeUtils.isArrayType(double[].class)); + Assert.assertTrue(TypeUtils.isArrayType(Object[].class)); + Assert.assertTrue(TypeUtils.isArrayType(String[].class)); + + Assert.assertFalse(TypeUtils.isArrayType(boolean.class)); + Assert.assertFalse(TypeUtils.isArrayType(byte.class)); + Assert.assertFalse(TypeUtils.isArrayType(short.class)); + Assert.assertFalse(TypeUtils.isArrayType(int.class)); + Assert.assertFalse(TypeUtils.isArrayType(char.class)); + Assert.assertFalse(TypeUtils.isArrayType(long.class)); + Assert.assertFalse(TypeUtils.isArrayType(float.class)); + Assert.assertFalse(TypeUtils.isArrayType(double.class)); + Assert.assertFalse(TypeUtils.isArrayType(Object.class)); + Assert.assertFalse(TypeUtils.isArrayType(String.class)); + } + + @Test + public void testIsArrayGenericTypes() throws Exception { + Method method = getClass().getMethod("dummyMethod", List.class, List.class, List.class, + List.class, List.class, List.class, List.class, List[].class, List[].class, + List[].class, List[].class, List[].class, List[].class, List[].class); + + Type[] types = method.getGenericParameterTypes(); + + Assert.assertFalse(TypeUtils.isArrayType(types[0])); + Assert.assertFalse(TypeUtils.isArrayType(types[1])); + Assert.assertFalse(TypeUtils.isArrayType(types[2])); + Assert.assertFalse(TypeUtils.isArrayType(types[3])); + Assert.assertFalse(TypeUtils.isArrayType(types[4])); + Assert.assertFalse(TypeUtils.isArrayType(types[5])); + Assert.assertFalse(TypeUtils.isArrayType(types[6])); + Assert.assertTrue(TypeUtils.isArrayType(types[7])); + Assert.assertTrue(TypeUtils.isArrayType(types[8])); + Assert.assertTrue(TypeUtils.isArrayType(types[9])); + Assert.assertTrue(TypeUtils.isArrayType(types[10])); + Assert.assertTrue(TypeUtils.isArrayType(types[11])); + Assert.assertTrue(TypeUtils.isArrayType(types[12])); + Assert.assertTrue(TypeUtils.isArrayType(types[13])); + } + + @Test + public void testGetPrimitiveArrayComponentType() throws Exception { + Assert.assertEquals(boolean.class, TypeUtils.getArrayComponentType(boolean[].class)); + Assert.assertEquals(byte.class, TypeUtils.getArrayComponentType(byte[].class)); + Assert.assertEquals(short.class, TypeUtils.getArrayComponentType(short[].class)); + Assert.assertEquals(int.class, TypeUtils.getArrayComponentType(int[].class)); + Assert.assertEquals(char.class, TypeUtils.getArrayComponentType(char[].class)); + Assert.assertEquals(long.class, TypeUtils.getArrayComponentType(long[].class)); + Assert.assertEquals(float.class, TypeUtils.getArrayComponentType(float[].class)); + Assert.assertEquals(double.class, TypeUtils.getArrayComponentType(double[].class)); + + Assert.assertNull(TypeUtils.getArrayComponentType(boolean.class)); + Assert.assertNull(TypeUtils.getArrayComponentType(byte.class)); + Assert.assertNull(TypeUtils.getArrayComponentType(short.class)); + Assert.assertNull(TypeUtils.getArrayComponentType(int.class)); + Assert.assertNull(TypeUtils.getArrayComponentType(char.class)); + Assert.assertNull(TypeUtils.getArrayComponentType(long.class)); + Assert.assertNull(TypeUtils.getArrayComponentType(float.class)); + Assert.assertNull(TypeUtils.getArrayComponentType(double.class)); + } + + @Test + public void testGetArrayComponentType() throws Exception { + Method method = getClass().getMethod("dummyMethod", List.class, List.class, List.class, + List.class, List.class, List.class, List.class, List[].class, List[].class, + List[].class, List[].class, List[].class, List[].class, List[].class); + + Type[] types = method.getGenericParameterTypes(); + + Assert.assertNull(TypeUtils.getArrayComponentType(types[0])); + Assert.assertNull(TypeUtils.getArrayComponentType(types[1])); + Assert.assertNull(TypeUtils.getArrayComponentType(types[2])); + Assert.assertNull(TypeUtils.getArrayComponentType(types[3])); + Assert.assertNull(TypeUtils.getArrayComponentType(types[4])); + Assert.assertNull(TypeUtils.getArrayComponentType(types[5])); + Assert.assertNull(TypeUtils.getArrayComponentType(types[6])); + Assert.assertEquals(types[0], TypeUtils.getArrayComponentType(types[7])); + Assert.assertEquals(types[1], TypeUtils.getArrayComponentType(types[8])); + Assert.assertEquals(types[2], TypeUtils.getArrayComponentType(types[9])); + Assert.assertEquals(types[3], TypeUtils.getArrayComponentType(types[10])); + Assert.assertEquals(types[4], TypeUtils.getArrayComponentType(types[11])); + Assert.assertEquals(types[5], TypeUtils.getArrayComponentType(types[12])); + Assert.assertEquals(types[6], TypeUtils.getArrayComponentType(types[13])); + } + + public Iterable>> iterable; + + public static > G stub() { + return null; + } + + public static > G stub2() { + return null; + } + + public static > T stub3() { + return null; + } +} + +class AAClass { + + public class BBClass { + } +} + +class AAAClass extends AAClass { + public class BBBClass extends BBClass { + } +} + +@SuppressWarnings("rawtypes") +//raw types, where used, are used purposely +class AClass extends AAClass.BBClass { + + public AClass(AAClass enclosingInstance) { + enclosingInstance.super(); + } + + public class BClass { + } + + public class CClass extends BClass { + } + + public class DClass extends CClass { + } + + public class EClass extends DClass { + } + + public class FClass extends EClass { + } + + public class GClass & AInterface>> { + } + + public BClass bClass; + + public CClass cClass; + + public DClass dClass; + + public EClass eClass; + + public FClass fClass; + + public GClass gClass; + + public interface AInterface { + } +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/reflect/package.html b/ApacheCommonsLang/org/apache/commons/lang3/reflect/package.html new file mode 100644 index 0000000..ea362c9 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/reflect/package.html @@ -0,0 +1,29 @@ + + + + + + + +Accumulates common high-level uses of the java.lang.reflect APIs. +@since 3.0 +

These classes are immutable, and therefore thread-safe.

+ + diff --git a/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/Ambig.java b/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/Ambig.java new file mode 100644 index 0000000..64844f0 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/Ambig.java @@ -0,0 +1,5 @@ +package org.apache.commons.lang3.reflect.testbed; + +public class Ambig { + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/Foo.java b/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/Foo.java new file mode 100644 index 0000000..99887ef --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/Foo.java @@ -0,0 +1,7 @@ +package org.apache.commons.lang3.reflect.testbed; + +public class Foo { + + public static final Object VALUE = null; + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/GenericParent.java b/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/GenericParent.java new file mode 100644 index 0000000..3c8bb98 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/GenericParent.java @@ -0,0 +1,5 @@ +package org.apache.commons.lang3.reflect.testbed; + +public class GenericParent { + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/GenericTypeHolder.java b/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/GenericTypeHolder.java new file mode 100644 index 0000000..1110750 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/GenericTypeHolder.java @@ -0,0 +1,5 @@ +package org.apache.commons.lang3.reflect.testbed; + +public class GenericTypeHolder { + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/PrivatelyShadowedChild.java b/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/PrivatelyShadowedChild.java new file mode 100644 index 0000000..cbf7f47 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/PrivatelyShadowedChild.java @@ -0,0 +1,5 @@ +package org.apache.commons.lang3.reflect.testbed; + +public class PrivatelyShadowedChild { + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/PublicChild.java b/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/PublicChild.java new file mode 100644 index 0000000..13355f6 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/PublicChild.java @@ -0,0 +1,5 @@ +package org.apache.commons.lang3.reflect.testbed; + +public class PublicChild { + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/PubliclyShadowedChild.java b/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/PubliclyShadowedChild.java new file mode 100644 index 0000000..dcee3bb --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/PubliclyShadowedChild.java @@ -0,0 +1,5 @@ +package org.apache.commons.lang3.reflect.testbed; + +public class PubliclyShadowedChild { + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/StaticContainer.java b/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/StaticContainer.java new file mode 100644 index 0000000..0a3c8ea --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/StaticContainer.java @@ -0,0 +1,27 @@ +package org.apache.commons.lang3.reflect.testbed; + +public class StaticContainer { + + public static Object mutablePublic; + + public static void reset() { + // TODO Auto-generated method stub + + } + + public static Object getMutableProtected() { + // TODO Auto-generated method stub + return null; + } + + public static Object getMutablePackage() { + // TODO Auto-generated method stub + return null; + } + + public static Object getMutablePrivate() { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/StaticContainerChild.java b/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/StaticContainerChild.java new file mode 100644 index 0000000..278d10b --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/StaticContainerChild.java @@ -0,0 +1,5 @@ +package org.apache.commons.lang3.reflect.testbed; + +public class StaticContainerChild { + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/StringParameterizedChild.java b/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/StringParameterizedChild.java new file mode 100644 index 0000000..706c20f --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/StringParameterizedChild.java @@ -0,0 +1,5 @@ +package org.apache.commons.lang3.reflect.testbed; + +public class StringParameterizedChild { + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/test/Ambig.java b/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/test/Ambig.java new file mode 100644 index 0000000..307f90e --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/test/Ambig.java @@ -0,0 +1,23 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.reflect.testbed.test; + +/** + * @version $Id: Ambig.java 1088899 2011-04-05 05:31:27Z bayard $ + */ +public class Ambig implements Foo, Bar { +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/test/Bar.java b/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/test/Bar.java new file mode 100644 index 0000000..7b38e2c --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/test/Bar.java @@ -0,0 +1,24 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.reflect.testbed.test; + +/** + * @version $Id: Bar.java 1088899 2011-04-05 05:31:27Z bayard $ + */ +public interface Bar { + public static final String VALUE = "bar"; +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/test/Foo.java b/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/test/Foo.java new file mode 100644 index 0000000..612972f --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/test/Foo.java @@ -0,0 +1,24 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.reflect.testbed.test; + +/** + * @version $Id: Foo.java 1088899 2011-04-05 05:31:27Z bayard $ + */ +public interface Foo { + public static final String VALUE = "foo"; +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/test/GenericParent.java b/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/test/GenericParent.java new file mode 100644 index 0000000..fa8c843 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/test/GenericParent.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.reflect.testbed.test; + +/** + * Class declaring a parameter variable. + * @version $Id: GenericParent.java 1088899 2011-04-05 05:31:27Z bayard $ + */ +public class GenericParent { + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/test/GenericTypeHolder.java b/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/test/GenericTypeHolder.java new file mode 100644 index 0000000..e35e596 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/test/GenericTypeHolder.java @@ -0,0 +1,30 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.reflect.testbed.test; + +import java.util.List; + +/** + * Holds generic testbed types. + * @version $Id: GenericTypeHolder.java 1088899 2011-04-05 05:31:27Z bayard $ + */ +public class GenericTypeHolder { + public GenericParent stringParent; + public GenericParent integerParent; + public List foos; + public GenericParent[] barParents; +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/test/Parent.java b/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/test/Parent.java new file mode 100644 index 0000000..0f60b9e --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/test/Parent.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.reflect.testbed.test; + +/** + * @version $Id: Parent.java 1088899 2011-04-05 05:31:27Z bayard $ + */ +class Parent implements Foo { + public String s = "s"; + protected boolean b = false; + int i = 0; + @SuppressWarnings("unused") + private double d = 0.0; +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/test/PrivatelyShadowedChild.java b/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/test/PrivatelyShadowedChild.java new file mode 100644 index 0000000..d4d66d4 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/test/PrivatelyShadowedChild.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.reflect.testbed.test; + +/** + * @version $Id: PrivatelyShadowedChild.java 1088899 2011-04-05 05:31:27Z bayard $ + */ +@SuppressWarnings({ "unused", "hiding" }) // deliberate re-use of variable names +public class PrivatelyShadowedChild extends Parent { + private String s = "ss"; + private boolean b = true; + private int i = 1; + private double d = 1.0; +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/test/PublicChild.java b/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/test/PublicChild.java new file mode 100644 index 0000000..9cbdcd8 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/test/PublicChild.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.reflect.testbed.test; + +/** + * @version $Id: PublicChild.java 1088899 2011-04-05 05:31:27Z bayard $ + */ +public class PublicChild extends Parent { + @SuppressWarnings("hiding") // deliberate reuse of variable name + static final String VALUE = "child"; +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/test/PubliclyShadowedChild.java b/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/test/PubliclyShadowedChild.java new file mode 100644 index 0000000..26ee407 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/test/PubliclyShadowedChild.java @@ -0,0 +1,28 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.reflect.testbed.test; + +/** + * @version $Id: PubliclyShadowedChild.java 1088899 2011-04-05 05:31:27Z bayard $ + */ +@SuppressWarnings("hiding") // deliberate re-use of variable names +public class PubliclyShadowedChild extends Parent { + public String s = "ss"; + public boolean b = true; + public int i = 1; + public double d = 1.0; +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/test/StaticContainer.java b/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/test/StaticContainer.java new file mode 100644 index 0000000..9f5666b --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/test/StaticContainer.java @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.reflect.testbed.test; + +/** + * @version $Id: StaticContainer.java 1088899 2011-04-05 05:31:27Z bayard $ + */ +public class StaticContainer { + public static final Object IMMUTABLE_PUBLIC = "public"; + protected static final Object IMMUTABLE_PROTECTED = "protected"; + static final Object IMMUTABLE_PACKAGE = ""; + @SuppressWarnings("unused") + private static final Object IMMUTABLE_PRIVATE = "private"; + + public static Object mutablePublic; + protected static Object mutableProtected; + static Object mutablePackage; + private static Object mutablePrivate; + + public static void reset() { + mutablePublic = null; + mutableProtected = null; + mutablePackage = null; + mutablePrivate = null; + } + + public static Object getMutableProtected() { + return mutableProtected; + } + + public static Object getMutablePackage() { + return mutablePackage; + } + + public static Object getMutablePrivate() { + return mutablePrivate; + } +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/test/StaticContainerChild.java b/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/test/StaticContainerChild.java new file mode 100644 index 0000000..70f2604 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/test/StaticContainerChild.java @@ -0,0 +1,24 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.reflect.testbed.test; + +/** + * @version $Id: StaticContainerChild.java 1088899 2011-04-05 05:31:27Z bayard $ + */ +public class StaticContainerChild extends StaticContainer { + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/test/StringParameterizedChild.java b/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/test/StringParameterizedChild.java new file mode 100644 index 0000000..d13976d --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/reflect/testbed/test/StringParameterizedChild.java @@ -0,0 +1,25 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.reflect.testbed.test; + +/** + * {@link GenericParent} subclass that explicitly specifies as {@link String}. + * @version $Id: StringParameterizedChild.java 1088899 2011-04-05 05:31:27Z bayard $ + */ +public class StringParameterizedChild extends GenericParent { + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/text/CompositeFormat.java b/ApacheCommonsLang/org/apache/commons/lang3/text/CompositeFormat.java new file mode 100644 index 0000000..2b058eb --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/text/CompositeFormat.java @@ -0,0 +1,116 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.text; + +import java.text.FieldPosition; +import java.text.Format; +import java.text.ParseException; +import java.text.ParsePosition; + +/** + * Formats using one formatter and parses using a different formatter. An + * example of use for this would be a webapp where data is taken in one way and + * stored in a database another way. + * + * @version $Id: CompositeFormat.java 1088899 2011-04-05 05:31:27Z bayard $ + */ +public class CompositeFormat extends Format { + + /** + * Required for serialization support. + * + * @see java.io.Serializable + */ + private static final long serialVersionUID = -4329119827877627683L; + + /** The parser to use. */ + private final Format parser; + /** The formatter to use. */ + private final Format formatter; + + /** + * Create a format that points its parseObject method to one implementation + * and its format method to another. + * + * @param parser implementation + * @param formatter implementation + */ + public CompositeFormat(Format parser, Format formatter) { + this.parser = parser; + this.formatter = formatter; + } + + /** + * Uses the formatter Format instance. + * + * @param obj the object to format + * @param toAppendTo the {@link StringBuffer} to append to + * @param pos the FieldPosition to use (or ignore). + * @return toAppendTo + * @see Format#format(Object, StringBuffer, FieldPosition) + */ + @Override + public StringBuffer format(Object obj, StringBuffer toAppendTo, + FieldPosition pos) { + return formatter.format(obj, toAppendTo, pos); + } + + /** + * Uses the parser Format instance. + * + * @param source the String source + * @param pos the ParsePosition containing the position to parse from, will + * be updated according to parsing success (index) or failure + * (error index) + * @return the parsed Object + * @see Format#parseObject(String, ParsePosition) + */ + @Override + public Object parseObject(String source, ParsePosition pos) { + return parser.parseObject(source, pos); + } + + /** + * Provides access to the parser Format implementation. + * + * @return parser Format implementation + */ + public Format getParser() { + return this.parser; + } + + /** + * Provides access to the parser Format implementation. + * + * @return formatter Format implementation + */ + public Format getFormatter() { + return this.formatter; + } + + /** + * Utility method to parse and then reformat a String. + * + * @param input String to reformat + * @return A reformatted String + * @throws ParseException thrown by parseObject(String) call + */ + public String reformat(String input) throws ParseException { + return format(parseObject(input)); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/text/CompositeFormatTest.java b/ApacheCommonsLang/org/apache/commons/lang3/text/CompositeFormatTest.java new file mode 100644 index 0000000..06a96a9 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/text/CompositeFormatTest.java @@ -0,0 +1,90 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.lang3.text; + +import java.text.FieldPosition; +import java.text.Format; +import java.text.ParsePosition; +import java.text.SimpleDateFormat; +import java.util.Locale; + +import junit.framework.TestCase; + +/** + * Unit tests for {@link org.apache.commons.lang3.text.CompositeFormat}. + */ +public class CompositeFormatTest extends TestCase { + + /** + * Create a new test case with the specified name. + * + * @param name + * name + */ + public CompositeFormatTest(String name) { + super(name); + } + + + /** + * Ensures that the parse/format separation is correctly maintained. + */ + public void testCompositeFormat() { + + Format parser = new Format() { + @Override + public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) { + throw new UnsupportedOperationException("Not implemented"); + } + + @Override + public Object parseObject(String source, ParsePosition pos) { + return null; // do nothing + } + }; + + Format formatter = new Format() { + @Override + public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) { + return null; // do nothing + } + + @Override + public Object parseObject(String source, ParsePosition pos) { + throw new UnsupportedOperationException("Not implemented"); + } + }; + + CompositeFormat composite = new CompositeFormat(parser, formatter); + + composite.parseObject("", null); + composite.format(new Object(), new StringBuffer(), null); + assertEquals( "Parser get method incorrectly implemented", parser, composite.getParser() ); + assertEquals( "Formatter get method incorrectly implemented", formatter, composite.getFormatter() ); + } + + public void testUsage() throws Exception { + Format f1 = new SimpleDateFormat("MMddyyyy", Locale.ENGLISH); + Format f2 = new SimpleDateFormat("MMMM d, yyyy", Locale.ENGLISH); + CompositeFormat c = new CompositeFormat(f1, f2); + String testString = "January 3, 2005"; + assertEquals(testString, c.format(c.parseObject("01032005"))); + assertEquals(testString, c.reformat("01032005")); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/text/ExtendedMessageFormat.java b/ApacheCommonsLang/org/apache/commons/lang3/text/ExtendedMessageFormat.java new file mode 100644 index 0000000..531155a --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/text/ExtendedMessageFormat.java @@ -0,0 +1,535 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.text; + +import java.text.Format; +import java.text.MessageFormat; +import java.text.ParsePosition; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.Locale; +import java.util.Map; + +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.Validate; + +/** + * Extends java.text.MessageFormat to allow pluggable/additional formatting + * options for embedded format elements. Client code should specify a registry + * of FormatFactory instances associated with String + * format names. This registry will be consulted when the format elements are + * parsed from the message pattern. In this way custom patterns can be specified, + * and the formats supported by java.text.MessageFormat can be overridden + * at the format and/or format style level (see MessageFormat). A "format element" + * embedded in the message pattern is specified (()? signifies optionality):
+ * {argument-number(,format-name + * (,format-style)?)?} + * + *

+ * format-name and format-style values are trimmed of surrounding whitespace + * in the manner of java.text.MessageFormat. If format-name denotes + * FormatFactory formatFactoryInstance in registry, a Format + * matching format-name and format-style is requested from + * formatFactoryInstance. If this is successful, the Format + * found is used for this format element. + *

+ * + *

NOTICE: The various subformat mutator methods are considered unnecessary; they exist on the parent + * class to allow the type of customization which it is the job of this class to provide in + * a configurable fashion. These methods have thus been disabled and will throw + * UnsupportedOperationException if called. + *

+ * + *

Limitations inherited from java.text.MessageFormat: + *

    + *
  • When using "choice" subformats, support for nested formatting instructions is limited + * to that provided by the base class.
  • + *
  • Thread-safety of Formats, including MessageFormat and thus + * ExtendedMessageFormat, is not guaranteed.
  • + *
+ *

+ * + * @since 2.4 + * @version $Id: ExtendedMessageFormat.java 1199983 2011-11-09 21:41:24Z ggregory $ + */ +public class ExtendedMessageFormat extends MessageFormat { + private static final long serialVersionUID = -2362048321261811743L; + private static final int HASH_SEED = 31; + + private static final String DUMMY_PATTERN = ""; + private static final String ESCAPED_QUOTE = "''"; + private static final char START_FMT = ','; + private static final char END_FE = '}'; + private static final char START_FE = '{'; + private static final char QUOTE = '\''; + + private String toPattern; + private final Map registry; + + /** + * Create a new ExtendedMessageFormat for the default locale. + * + * @param pattern the pattern to use, not null + * @throws IllegalArgumentException in case of a bad pattern. + */ + public ExtendedMessageFormat(String pattern) { + this(pattern, Locale.getDefault()); + } + + /** + * Create a new ExtendedMessageFormat. + * + * @param pattern the pattern to use, not null + * @param locale the locale to use, not null + * @throws IllegalArgumentException in case of a bad pattern. + */ + public ExtendedMessageFormat(String pattern, Locale locale) { + this(pattern, locale, null); + } + + /** + * Create a new ExtendedMessageFormat for the default locale. + * + * @param pattern the pattern to use, not null + * @param registry the registry of format factories, may be null + * @throws IllegalArgumentException in case of a bad pattern. + */ + public ExtendedMessageFormat(String pattern, Map registry) { + this(pattern, Locale.getDefault(), registry); + } + + /** + * Create a new ExtendedMessageFormat. + * + * @param pattern the pattern to use, not null + * @param locale the locale to use, not null + * @param registry the registry of format factories, may be null + * @throws IllegalArgumentException in case of a bad pattern. + */ + public ExtendedMessageFormat(String pattern, Locale locale, Map registry) { + super(DUMMY_PATTERN); + setLocale(locale); + this.registry = registry; + applyPattern(pattern); + } + + /** + * {@inheritDoc} + */ + @Override + public String toPattern() { + return toPattern; + } + + /** + * Apply the specified pattern. + * + * @param pattern String + */ + @Override + public final void applyPattern(String pattern) { + if (registry == null) { + super.applyPattern(pattern); + toPattern = super.toPattern(); + return; + } + ArrayList foundFormats = new ArrayList(); + ArrayList foundDescriptions = new ArrayList(); + StringBuilder stripCustom = new StringBuilder(pattern.length()); + + ParsePosition pos = new ParsePosition(0); + char[] c = pattern.toCharArray(); + int fmtCount = 0; + while (pos.getIndex() < pattern.length()) { + switch (c[pos.getIndex()]) { + case QUOTE: + appendQuotedString(pattern, pos, stripCustom, true); + break; + case START_FE: + fmtCount++; + seekNonWs(pattern, pos); + int start = pos.getIndex(); + int index = readArgumentIndex(pattern, next(pos)); + stripCustom.append(START_FE).append(index); + seekNonWs(pattern, pos); + Format format = null; + String formatDescription = null; + if (c[pos.getIndex()] == START_FMT) { + formatDescription = parseFormatDescription(pattern, + next(pos)); + format = getFormat(formatDescription); + if (format == null) { + stripCustom.append(START_FMT).append(formatDescription); + } + } + foundFormats.add(format); + foundDescriptions.add(format == null ? null : formatDescription); + Validate.isTrue(foundFormats.size() == fmtCount); + Validate.isTrue(foundDescriptions.size() == fmtCount); + if (c[pos.getIndex()] != END_FE) { + throw new IllegalArgumentException( + "Unreadable format element at position " + start); + } + //$FALL-THROUGH$ + default: + stripCustom.append(c[pos.getIndex()]); + next(pos); + } + } + super.applyPattern(stripCustom.toString()); + toPattern = insertFormats(super.toPattern(), foundDescriptions); + if (containsElements(foundFormats)) { + Format[] origFormats = getFormats(); + // only loop over what we know we have, as MessageFormat on Java 1.3 + // seems to provide an extra format element: + int i = 0; + for (Iterator it = foundFormats.iterator(); it.hasNext(); i++) { + Format f = it.next(); + if (f != null) { + origFormats[i] = f; + } + } + super.setFormats(origFormats); + } + } + + /** + * Throws UnsupportedOperationException - see class Javadoc for details. + * + * @param formatElementIndex format element index + * @param newFormat the new format + * @throws UnsupportedOperationException + */ + @Override + public void setFormat(int formatElementIndex, Format newFormat) { + throw new UnsupportedOperationException(); + } + + /** + * Throws UnsupportedOperationException - see class Javadoc for details. + * + * @param argumentIndex argument index + * @param newFormat the new format + * @throws UnsupportedOperationException + */ + @Override + public void setFormatByArgumentIndex(int argumentIndex, Format newFormat) { + throw new UnsupportedOperationException(); + } + + /** + * Throws UnsupportedOperationException - see class Javadoc for details. + * + * @param newFormats new formats + * @throws UnsupportedOperationException + */ + @Override + public void setFormats(Format[] newFormats) { + throw new UnsupportedOperationException(); + } + + /** + * Throws UnsupportedOperationException - see class Javadoc for details. + * + * @param newFormats new formats + * @throws UnsupportedOperationException + */ + @Override + public void setFormatsByArgumentIndex(Format[] newFormats) { + throw new UnsupportedOperationException(); + } + + /** + * Check if this extended message format is equal to another object. + * + * @param obj the object to compare to + * @return true if this object equals the other, otherwise false + */ + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (obj == null) { + return false; + } + if (!super.equals(obj)) { + return false; + } + if (ObjectUtils.notEqual(getClass(), obj.getClass())) { + return false; + } + ExtendedMessageFormat rhs = (ExtendedMessageFormat)obj; + if (ObjectUtils.notEqual(toPattern, rhs.toPattern)) { + return false; + } + if (ObjectUtils.notEqual(registry, rhs.registry)) { + return false; + } + return true; + } + + /** + * Return the hashcode. + * + * @return the hashcode + */ + @Override + public int hashCode() { + int result = super.hashCode(); + result = HASH_SEED * result + ObjectUtils.hashCode(registry); + result = HASH_SEED * result + ObjectUtils.hashCode(toPattern); + return result; + } + + /** + * Get a custom format from a format description. + * + * @param desc String + * @return Format + */ + private Format getFormat(String desc) { + if (registry != null) { + String name = desc; + String args = null; + int i = desc.indexOf(START_FMT); + if (i > 0) { + name = desc.substring(0, i).trim(); + args = desc.substring(i + 1).trim(); + } + FormatFactory factory = registry.get(name); + if (factory != null) { + return factory.getFormat(name, args, getLocale()); + } + } + return null; + } + + /** + * Read the argument index from the current format element + * + * @param pattern pattern to parse + * @param pos current parse position + * @return argument index + */ + private int readArgumentIndex(String pattern, ParsePosition pos) { + int start = pos.getIndex(); + seekNonWs(pattern, pos); + StringBuffer result = new StringBuffer(); + boolean error = false; + for (; !error && pos.getIndex() < pattern.length(); next(pos)) { + char c = pattern.charAt(pos.getIndex()); + if (Character.isWhitespace(c)) { + seekNonWs(pattern, pos); + c = pattern.charAt(pos.getIndex()); + if (c != START_FMT && c != END_FE) { + error = true; + continue; + } + } + if ((c == START_FMT || c == END_FE) && result.length() > 0) { + try { + return Integer.parseInt(result.toString()); + } catch (NumberFormatException e) { // NOPMD + // we've already ensured only digits, so unless something + // outlandishly large was specified we should be okay. + } + } + error = !Character.isDigit(c); + result.append(c); + } + if (error) { + throw new IllegalArgumentException( + "Invalid format argument index at position " + start + ": " + + pattern.substring(start, pos.getIndex())); + } + throw new IllegalArgumentException( + "Unterminated format element at position " + start); + } + + /** + * Parse the format component of a format element. + * + * @param pattern string to parse + * @param pos current parse position + * @return Format description String + */ + private String parseFormatDescription(String pattern, ParsePosition pos) { + int start = pos.getIndex(); + seekNonWs(pattern, pos); + int text = pos.getIndex(); + int depth = 1; + for (; pos.getIndex() < pattern.length(); next(pos)) { + switch (pattern.charAt(pos.getIndex())) { + case START_FE: + depth++; + break; + case END_FE: + depth--; + if (depth == 0) { + return pattern.substring(text, pos.getIndex()); + } + break; + case QUOTE: + getQuotedString(pattern, pos, false); + break; + } + } + throw new IllegalArgumentException( + "Unterminated format element at position " + start); + } + + /** + * Insert formats back into the pattern for toPattern() support. + * + * @param pattern source + * @param customPatterns The custom patterns to re-insert, if any + * @return full pattern + */ + private String insertFormats(String pattern, ArrayList customPatterns) { + if (!containsElements(customPatterns)) { + return pattern; + } + StringBuilder sb = new StringBuilder(pattern.length() * 2); + ParsePosition pos = new ParsePosition(0); + int fe = -1; + int depth = 0; + while (pos.getIndex() < pattern.length()) { + char c = pattern.charAt(pos.getIndex()); + switch (c) { + case QUOTE: + appendQuotedString(pattern, pos, sb, false); + break; + case START_FE: + depth++; + if (depth == 1) { + fe++; + sb.append(START_FE).append( + readArgumentIndex(pattern, next(pos))); + String customPattern = customPatterns.get(fe); + if (customPattern != null) { + sb.append(START_FMT).append(customPattern); + } + } + break; + case END_FE: + depth--; + //$FALL-THROUGH$ + default: + sb.append(c); + next(pos); + } + } + return sb.toString(); + } + + /** + * Consume whitespace from the current parse position. + * + * @param pattern String to read + * @param pos current position + */ + private void seekNonWs(String pattern, ParsePosition pos) { + int len = 0; + char[] buffer = pattern.toCharArray(); + do { + len = StrMatcher.splitMatcher().isMatch(buffer, pos.getIndex()); + pos.setIndex(pos.getIndex() + len); + } while (len > 0 && pos.getIndex() < pattern.length()); + } + + /** + * Convenience method to advance parse position by 1 + * + * @param pos ParsePosition + * @return pos + */ + private ParsePosition next(ParsePosition pos) { + pos.setIndex(pos.getIndex() + 1); + return pos; + } + + /** + * Consume a quoted string, adding it to appendTo if + * specified. + * + * @param pattern pattern to parse + * @param pos current parse position + * @param appendTo optional StringBuffer to append + * @param escapingOn whether to process escaped quotes + * @return appendTo + */ + private StringBuilder appendQuotedString(String pattern, ParsePosition pos, + StringBuilder appendTo, boolean escapingOn) { + int start = pos.getIndex(); + char[] c = pattern.toCharArray(); + if (escapingOn && c[start] == QUOTE) { + next(pos); + return appendTo == null ? null : appendTo.append(QUOTE); + } + int lastHold = start; + for (int i = pos.getIndex(); i < pattern.length(); i++) { + if (escapingOn && pattern.substring(i).startsWith(ESCAPED_QUOTE)) { + appendTo.append(c, lastHold, pos.getIndex() - lastHold).append( + QUOTE); + pos.setIndex(i + ESCAPED_QUOTE.length()); + lastHold = pos.getIndex(); + continue; + } + switch (c[pos.getIndex()]) { + case QUOTE: + next(pos); + return appendTo == null ? null : appendTo.append(c, lastHold, + pos.getIndex() - lastHold); + default: + next(pos); + } + } + throw new IllegalArgumentException( + "Unterminated quoted string at position " + start); + } + + /** + * Consume quoted string only + * + * @param pattern pattern to parse + * @param pos current parse position + * @param escapingOn whether to process escaped quotes + */ + private void getQuotedString(String pattern, ParsePosition pos, + boolean escapingOn) { + appendQuotedString(pattern, pos, null, escapingOn); + } + + /** + * Learn whether the specified Collection contains non-null elements. + * @param coll to check + * @return true if some Object was found, false otherwise. + */ + private boolean containsElements(Collection coll) { + if (coll == null || coll.isEmpty()) { + return false; + } + for (Object name : coll) { + if (name != null) { + return true; + } + } + return false; + } +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/text/ExtendedMessageFormatTest.java b/ApacheCommonsLang/org/apache/commons/lang3/text/ExtendedMessageFormatTest.java new file mode 100644 index 0000000..c787fac --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/text/ExtendedMessageFormatTest.java @@ -0,0 +1,454 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.text; + +import static org.apache.commons.lang3.JavaVersion.JAVA_1_4; + +import java.text.ChoiceFormat; +import java.text.DateFormat; +import java.text.FieldPosition; +import java.text.Format; +import java.text.MessageFormat; +import java.text.NumberFormat; +import java.text.ParsePosition; +import java.util.Arrays; +import java.util.Calendar; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Locale; +import java.util.Map; + +import junit.framework.TestCase; + +import org.apache.commons.lang3.SystemUtils; + +/** + * Test case for {@link ExtendedMessageFormat}. + * + * @since 2.4 + * @version $Id: ExtendedMessageFormatTest.java 1309977 2012-04-05 17:53:39Z ggregory $ + */ +public class ExtendedMessageFormatTest extends TestCase { + + private final Map registry = new HashMap(); + + /** + * Create a new test case. + * + * @param name The name of the test + */ + public ExtendedMessageFormatTest(String name) { + super(name); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + registry.put("lower", new LowerCaseFormatFactory()); + registry.put("upper", new UpperCaseFormatFactory()); + } + + /** + * Test extended formats. + */ + public void testExtendedFormats() { + String pattern = "Lower: {0,lower} Upper: {1,upper}"; + ExtendedMessageFormat emf = new ExtendedMessageFormat(pattern, registry); + assertPatternsEqual("TOPATTERN", pattern, emf.toPattern()); + assertEquals("Lower: foo Upper: BAR", emf.format(new Object[] {"foo", "bar"})); + assertEquals("Lower: foo Upper: BAR", emf.format(new Object[] {"Foo", "Bar"})); + assertEquals("Lower: foo Upper: BAR", emf.format(new Object[] {"FOO", "BAR"})); + assertEquals("Lower: foo Upper: BAR", emf.format(new Object[] {"FOO", "bar"})); + assertEquals("Lower: foo Upper: BAR", emf.format(new Object[] {"foo", "BAR"})); + } + + /** + * Test Bug LANG-477 - out of memory error with escaped quote + */ + public void testEscapedQuote_LANG_477() { + String pattern = "it''s a {0,lower} 'test'!"; + ExtendedMessageFormat emf = new ExtendedMessageFormat(pattern, registry); + assertEquals("it's a dummy test!", emf.format(new Object[] {"DUMMY"})); + } + + /** + * Test extended and built in formats. + */ + public void testExtendedAndBuiltInFormats() { + Calendar cal = Calendar.getInstance(); + cal.set(2007, Calendar.JANUARY, 23, 18, 33, 05); + Object[] args = new Object[] {"John Doe", cal.getTime(), Double.valueOf("12345.67")}; + String builtinsPattern = "DOB: {1,date,short} Salary: {2,number,currency}"; + String extendedPattern = "Name: {0,upper} "; + String pattern = extendedPattern + builtinsPattern; + + HashSet testLocales = new HashSet(); + testLocales.addAll(Arrays.asList(DateFormat.getAvailableLocales())); + testLocales.retainAll(Arrays.asList(NumberFormat.getAvailableLocales())); + testLocales.add(null); + + for (Locale locale : testLocales) { + MessageFormat builtins = createMessageFormat(builtinsPattern, locale); + String expectedPattern = extendedPattern + builtins.toPattern(); + DateFormat df = null; + NumberFormat nf = null; + ExtendedMessageFormat emf = null; + if (locale == null) { + df = DateFormat.getDateInstance(DateFormat.SHORT); + nf = NumberFormat.getCurrencyInstance(); + emf = new ExtendedMessageFormat(pattern, registry); + } else { + df = DateFormat.getDateInstance(DateFormat.SHORT, locale); + nf = NumberFormat.getCurrencyInstance(locale); + emf = new ExtendedMessageFormat(pattern, locale, registry); + } + StringBuffer expected = new StringBuffer(); + expected.append("Name: "); + expected.append(args[0].toString().toUpperCase()); + expected.append(" DOB: "); + expected.append(df.format(args[1])); + expected.append(" Salary: "); + expected.append(nf.format(args[2])); + assertPatternsEqual("pattern comparison for locale " + locale, expectedPattern, emf.toPattern()); + assertEquals(String.valueOf(locale), expected.toString(), emf.format(args)); + } + } + +// /** +// * Test extended formats with choice format. +// * +// * NOTE: FAILING - currently sub-formats not supported +// */ +// public void testExtendedWithChoiceFormat() { +// String pattern = "Choice: {0,choice,1.0#{1,lower}|2.0#{1,upper}}"; +// ExtendedMessageFormat emf = new ExtendedMessageFormat(pattern, registry); +// assertPatterns(null, pattern, emf.toPattern()); +// try { +// assertEquals("one", emf.format(new Object[] {Integer.valueOf(1), "ONE"})); +// assertEquals("TWO", emf.format(new Object[] {Integer.valueOf(2), "two"})); +// } catch (IllegalArgumentException e) { +// // currently sub-formats not supported +// } +// } + +// /** +// * Test mixed extended and built-in formats with choice format. +// * +// * NOTE: FAILING - currently sub-formats not supported +// */ +// public void testExtendedAndBuiltInWithChoiceFormat() { +// String pattern = "Choice: {0,choice,1.0#{0} {1,lower} {2,number}|2.0#{0} {1,upper} {2,number,currency}}"; +// Object[] lowArgs = new Object[] {Integer.valueOf(1), "Low", Double.valueOf("1234.56")}; +// Object[] highArgs = new Object[] {Integer.valueOf(2), "High", Double.valueOf("9876.54")}; +// Locale[] availableLocales = ChoiceFormat.getAvailableLocales(); +// Locale[] testLocales = new Locale[availableLocales.length + 1]; +// testLocales[0] = null; +// System.arraycopy(availableLocales, 0, testLocales, 1, availableLocales.length); +// for (int i = 0; i < testLocales.length; i++) { +// NumberFormat nf = null; +// NumberFormat cf = null; +// ExtendedMessageFormat emf = null; +// if (testLocales[i] == null) { +// nf = NumberFormat.getNumberInstance(); +// cf = NumberFormat.getCurrencyInstance(); +// emf = new ExtendedMessageFormat(pattern, registry); +// } else { +// nf = NumberFormat.getNumberInstance(testLocales[i]); +// cf = NumberFormat.getCurrencyInstance(testLocales[i]); +// emf = new ExtendedMessageFormat(pattern, testLocales[i], registry); +// } +// assertPatterns(null, pattern, emf.toPattern()); +// try { +// String lowExpected = lowArgs[0] + " low " + nf.format(lowArgs[2]); +// String highExpected = highArgs[0] + " HIGH " + cf.format(highArgs[2]); +// assertEquals(lowExpected, emf.format(lowArgs)); +// assertEquals(highExpected, emf.format(highArgs)); +// } catch (IllegalArgumentException e) { +// // currently sub-formats not supported +// } +// } +// } + + /** + * Test the built in choice format. + */ + public void testBuiltInChoiceFormat() { + Object[] values = new Number[] {Integer.valueOf(1), Double.valueOf("2.2"), Double.valueOf("1234.5")}; + String choicePattern = null; + Locale[] availableLocales = ChoiceFormat.getAvailableLocales(); + + choicePattern = "{0,choice,1#One|2#Two|3#Many {0,number}}"; + for (Object value : values) { + checkBuiltInFormat(value + ": " + choicePattern, new Object[] {value}, availableLocales); + } + + choicePattern = "{0,choice,1#''One''|2#\"Two\"|3#''{Many}'' {0,number}}"; + for (Object value : values) { + checkBuiltInFormat(value + ": " + choicePattern, new Object[] {value}, availableLocales); + } + } + + /** + * Test the built in date/time formats + */ + public void testBuiltInDateTimeFormat() { + Calendar cal = Calendar.getInstance(); + cal.set(2007, Calendar.JANUARY, 23, 18, 33, 05); + Object[] args = new Object[] {cal.getTime()}; + Locale[] availableLocales = DateFormat.getAvailableLocales(); + + checkBuiltInFormat("1: {0,date,short}", args, availableLocales); + checkBuiltInFormat("2: {0,date,medium}", args, availableLocales); + checkBuiltInFormat("3: {0,date,long}", args, availableLocales); + checkBuiltInFormat("4: {0,date,full}", args, availableLocales); + checkBuiltInFormat("5: {0,date,d MMM yy}", args, availableLocales); + checkBuiltInFormat("6: {0,time,short}", args, availableLocales); + checkBuiltInFormat("7: {0,time,medium}", args, availableLocales); + checkBuiltInFormat("8: {0,time,long}", args, availableLocales); + checkBuiltInFormat("9: {0,time,full}", args, availableLocales); + checkBuiltInFormat("10: {0,time,HH:mm}", args, availableLocales); + checkBuiltInFormat("11: {0,date}", args, availableLocales); + checkBuiltInFormat("12: {0,time}", args, availableLocales); + } + + public void testOverriddenBuiltinFormat() { + Calendar cal = Calendar.getInstance(); + cal.set(2007, Calendar.JANUARY, 23); + Object[] args = new Object[] {cal.getTime()}; + Locale[] availableLocales = DateFormat.getAvailableLocales(); + Map registry = Collections.singletonMap("date", new OverrideShortDateFormatFactory()); + + //check the non-overridden builtins: + checkBuiltInFormat("1: {0,date}", registry, args, availableLocales); + checkBuiltInFormat("2: {0,date,medium}", registry, args, availableLocales); + checkBuiltInFormat("3: {0,date,long}", registry, args, availableLocales); + checkBuiltInFormat("4: {0,date,full}", registry, args, availableLocales); + checkBuiltInFormat("5: {0,date,d MMM yy}", registry, args, availableLocales); + + //check the overridden format: + for (int i = -1; i < availableLocales.length; i++) { + Locale locale = i < 0 ? null : availableLocales[i]; + MessageFormat dateDefault = createMessageFormat("{0,date}", locale); + String pattern = "{0,date,short}"; + ExtendedMessageFormat dateShort = new ExtendedMessageFormat(pattern, locale, registry); + assertEquals("overridden date,short format", dateDefault.format(args), dateShort.format(args)); + assertEquals("overridden date,short pattern", pattern, dateShort.toPattern()); + } + } + + /** + * Test the built in number formats. + */ + public void testBuiltInNumberFormat() { + Object[] args = new Object[] {Double.valueOf("6543.21")}; + Locale[] availableLocales = NumberFormat.getAvailableLocales(); + checkBuiltInFormat("1: {0,number}", args, availableLocales); + checkBuiltInFormat("2: {0,number,integer}", args, availableLocales); + checkBuiltInFormat("3: {0,number,currency}", args, availableLocales); + checkBuiltInFormat("4: {0,number,percent}", args, availableLocales); + checkBuiltInFormat("5: {0,number,00000.000}", args, availableLocales); + } + + /** + * Test equals() and hashcode. + */ + public void testEqualsHashcode() { + Map registry = Collections.singletonMap("testfmt", new LowerCaseFormatFactory()); + Map otherRegitry = Collections.singletonMap("testfmt", new UpperCaseFormatFactory()); + + String pattern = "Pattern: {0,testfmt}"; + ExtendedMessageFormat emf = new ExtendedMessageFormat(pattern, Locale.US, registry); + + ExtendedMessageFormat other = null; + + // Same object + assertTrue("same, equals()", emf.equals(emf)); + assertTrue("same, hashcode()", emf.hashCode() == emf.hashCode()); + + // Equal Object + other = new ExtendedMessageFormat(pattern, Locale.US, registry); + assertTrue("equal, equals()", emf.equals(other)); + assertTrue("equal, hashcode()", emf.hashCode() == other.hashCode()); + + // Different Class + other = new OtherExtendedMessageFormat(pattern, Locale.US, registry); + assertFalse("class, equals()", emf.equals(other)); + assertTrue("class, hashcode()", emf.hashCode() == other.hashCode()); // same hashcode + + // Different pattern + other = new ExtendedMessageFormat("X" + pattern, Locale.US, registry); + assertFalse("pattern, equals()", emf.equals(other)); + assertFalse("pattern, hashcode()", emf.hashCode() == other.hashCode()); + + // Different registry + other = new ExtendedMessageFormat(pattern, Locale.US, otherRegitry); + assertFalse("registry, equals()", emf.equals(other)); + assertFalse("registry, hashcode()", emf.hashCode() == other.hashCode()); + + // Different Locale + other = new ExtendedMessageFormat(pattern, Locale.FRANCE, registry); + assertFalse("locale, equals()", emf.equals(other)); + assertTrue("locale, hashcode()", emf.hashCode() == other.hashCode()); // same hashcode + } + + /** + * Test a built in format for the specified Locales, plus null Locale. + * @param pattern MessageFormat pattern + * @param args MessageFormat arguments + * @param locales to test + */ + private void checkBuiltInFormat(String pattern, Object[] args, Locale[] locales) { + checkBuiltInFormat(pattern, null, args, locales); + } + + /** + * Test a built in format for the specified Locales, plus null Locale. + * @param pattern MessageFormat pattern + * @param registry FormatFactory registry to use + * @param args MessageFormat arguments + * @param locales to test + */ + private void checkBuiltInFormat(String pattern, Map registry, Object[] args, Locale[] locales) { + checkBuiltInFormat(pattern, registry, args, (Locale) null); + for (Locale locale : locales) { + checkBuiltInFormat(pattern, registry, args, locale); + } + } + + /** + * Create an ExtendedMessageFormat for the specified pattern and locale and check the + * formated output matches the expected result for the parameters. + * @param pattern string + * @param registry map + * @param args Object[] + * @param locale Locale + */ + private void checkBuiltInFormat(String pattern, Map registry, Object[] args, Locale locale) { + StringBuffer buffer = new StringBuffer(); + buffer.append("Pattern=["); + buffer.append(pattern); + buffer.append("], locale=["); + buffer.append(locale); + buffer.append("]"); + MessageFormat mf = createMessageFormat(pattern, locale); + // System.out.println(buffer + ", result=[" + mf.format(args) +"]"); + ExtendedMessageFormat emf = null; + if (locale == null) { + emf = new ExtendedMessageFormat(pattern); + } else { + emf = new ExtendedMessageFormat(pattern, locale); + } + assertEquals("format " + buffer.toString(), mf.format(args), emf.format(args)); + assertPatternsEqual("toPattern " + buffer.toString(), mf.toPattern(), emf.toPattern()); + } + + //can't trust what MessageFormat does with toPattern() pre 1.4: + private void assertPatternsEqual(String message, String expected, String actual) { + if (SystemUtils.isJavaVersionAtLeast(JAVA_1_4)) { + assertEquals(message, expected, actual); + } + } + + /** + * Replace MessageFormat(String, Locale) constructor (not available until JDK 1.4). + * @param pattern string + * @param locale Locale + * @return MessageFormat + */ + private MessageFormat createMessageFormat(String pattern, Locale locale) { + MessageFormat result = new MessageFormat(pattern); + if (locale != null) { + result.setLocale(locale); + result.applyPattern(pattern); + } + return result; + } + + // ------------------------ Test Formats ------------------------ + + /** + * {@link Format} implementation which converts to lower case. + */ + private static class LowerCaseFormat extends Format { + @Override + public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) { + return toAppendTo.append(((String)obj).toLowerCase()); + } + @Override + public Object parseObject(String source, ParsePosition pos) {throw new UnsupportedOperationException();} + } + + /** + * {@link Format} implementation which converts to upper case. + */ + private static class UpperCaseFormat extends Format { + @Override + public StringBuffer format(Object obj, StringBuffer toAppendTo, FieldPosition pos) { + return toAppendTo.append(((String)obj).toUpperCase()); + } + @Override + public Object parseObject(String source, ParsePosition pos) {throw new UnsupportedOperationException();} + } + + + // ------------------------ Test Format Factories --------------- + /** + * {@link FormatFactory} implementation for lower case format. + */ + private static class LowerCaseFormatFactory implements FormatFactory { + private static final Format LOWER_INSTANCE = new LowerCaseFormat(); + @Override + public Format getFormat(String name, String arguments, Locale locale) { + return LOWER_INSTANCE; + } + } + /** + * {@link FormatFactory} implementation for upper case format. + */ + private static class UpperCaseFormatFactory implements FormatFactory { + private static final Format UPPER_INSTANCE = new UpperCaseFormat(); + @Override + public Format getFormat(String name, String arguments, Locale locale) { + return UPPER_INSTANCE; + } + } + /** + * {@link FormatFactory} implementation to override date format "short" to "default". + */ + private static class OverrideShortDateFormatFactory implements FormatFactory { + @Override + public Format getFormat(String name, String arguments, Locale locale) { + return !"short".equals(arguments) ? null + : locale == null ? DateFormat + .getDateInstance(DateFormat.DEFAULT) : DateFormat + .getDateInstance(DateFormat.DEFAULT, locale); + } + } + + /** + * Alternative ExtendedMessageFormat impl. + */ + private static class OtherExtendedMessageFormat extends ExtendedMessageFormat { + public OtherExtendedMessageFormat(String pattern, Locale locale, + Map registry) { + super(pattern, locale, registry); + } + + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/text/FormatFactory.java b/ApacheCommonsLang/org/apache/commons/lang3/text/FormatFactory.java new file mode 100644 index 0000000..d2d5944 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/text/FormatFactory.java @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.text; + +import java.text.Format; +import java.util.Locale; + +/** + * Format factory. + * + * @since 2.4 + * @version $Id: FormatFactory.java 1088899 2011-04-05 05:31:27Z bayard $ + */ +public interface FormatFactory { + + /** + * Create or retrieve a format instance. + * + * @param name The format type name + * @param arguments Arguments used to create the format instance. This allows the + * FormatFactory to implement the "format style" + * concept from java.text.MessageFormat. + * @param locale The locale, may be null + * @return The format instance + */ + Format getFormat(String name, String arguments, Locale locale); + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/text/FormattableUtils.java b/ApacheCommonsLang/org/apache/commons/lang3/text/FormattableUtils.java new file mode 100644 index 0000000..fb84e09 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/text/FormattableUtils.java @@ -0,0 +1,151 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.text; + +import static java.util.FormattableFlags.LEFT_JUSTIFY; + +import java.util.Formattable; +import java.util.Formatter; + +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.Validate; + +/** + *

Provides utilities for working with the {@code Formattable} interface.

+ * + *

The {@link Formattable} interface provides basic control over formatting + * when using a {@code Formatter}. It is primarily concerned with numeric precision + * and padding, and is not designed to allow generalised alternate formats.

+ * + * @since Lang 3.0 + * @version $Id: FormattableUtils.java 1132390 2011-06-05 12:45:10Z sebb $ + */ +public class FormattableUtils { + + /** + * A format that simply outputs the value as a string. + */ + private static final String SIMPLEST_FORMAT = "%s"; + + /** + *

{@code FormattableUtils} instances should NOT be constructed in + * standard programming. Instead, the methods of the class should be invoked + * statically.

+ * + *

This constructor is public to permit tools that require a JavaBean + * instance to operate.

+ */ + public FormattableUtils() { + super(); + } + + //----------------------------------------------------------------------- + /** + * Get the default formatted representation of the specified + * {@code Formattable}. + * + * @param formattable the instance to convert to a string, not null + * @return the resulting string, not null + */ + public static String toString(Formattable formattable) { + return String.format(SIMPLEST_FORMAT, formattable); + } + + /** + * Handles the common {@code Formattable} operations of truncate-pad-append, + * with no ellipsis on precision overflow, and padding width underflow with + * spaces. + * + * @param seq the string to handle, not null + * @param formatter the destination formatter, not null + * @param flags the flags for formatting, see {@code Formattable} + * @param width the width of the output, see {@code Formattable} + * @param precision the precision of the output, see {@code Formattable} + * @return the {@code formatter} instance, not null + */ + public static Formatter append(CharSequence seq, Formatter formatter, int flags, int width, + int precision) { + return append(seq, formatter, flags, width, precision, ' ', null); + } + + /** + * Handles the common {@link Formattable} operations of truncate-pad-append, + * with no ellipsis on precision overflow. + * + * @param seq the string to handle, not null + * @param formatter the destination formatter, not null + * @param flags the flags for formatting, see {@code Formattable} + * @param width the width of the output, see {@code Formattable} + * @param precision the precision of the output, see {@code Formattable} + * @param padChar the pad character to use + * @return the {@code formatter} instance, not null + */ + public static Formatter append(CharSequence seq, Formatter formatter, int flags, int width, + int precision, char padChar) { + return append(seq, formatter, flags, width, precision, padChar, null); + } + + /** + * Handles the common {@link Formattable} operations of truncate-pad-append, + * padding width underflow with spaces. + * + * @param seq the string to handle, not null + * @param formatter the destination formatter, not null + * @param flags the flags for formatting, see {@code Formattable} + * @param width the width of the output, see {@code Formattable} + * @param precision the precision of the output, see {@code Formattable} + * @param ellipsis the ellipsis to use when precision dictates truncation, null or + * empty causes a hard truncation + * @return the {@code formatter} instance, not null + */ + public static Formatter append(CharSequence seq, Formatter formatter, int flags, int width, + int precision, CharSequence ellipsis) { + return append(seq, formatter, flags, width, precision, ' ', ellipsis); + } + + /** + * Handles the common {@link Formattable} operations of truncate-pad-append. + * + * @param seq the string to handle, not null + * @param formatter the destination formatter, not null + * @param flags the flags for formatting, see {@code Formattable} + * @param width the width of the output, see {@code Formattable} + * @param precision the precision of the output, see {@code Formattable} + * @param padChar the pad character to use + * @param ellipsis the ellipsis to use when precision dictates truncation, null or + * empty causes a hard truncation + * @return the {@code formatter} instance, not null + */ + public static Formatter append(CharSequence seq, Formatter formatter, int flags, int width, + int precision, char padChar, CharSequence ellipsis) { + Validate.isTrue(ellipsis == null || precision < 0 || ellipsis.length() <= precision, + "Specified ellipsis '%1$s' exceeds precision of %2$s", ellipsis, Integer.valueOf(precision)); + StringBuilder buf = new StringBuilder(seq); + if (precision >= 0 && precision < seq.length()) { + CharSequence _ellipsis = ObjectUtils.defaultIfNull(ellipsis, StringUtils.EMPTY); + buf.replace(precision - _ellipsis.length(), seq.length(), _ellipsis.toString()); + } + boolean leftJustify = (flags & LEFT_JUSTIFY) == LEFT_JUSTIFY; + for (int i = buf.length(); i < width; i++) { + buf.insert(leftJustify ? i : 0, padChar); + } + formatter.format(buf.toString()); + return formatter; + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/text/FormattableUtilsTest.java b/ApacheCommonsLang/org/apache/commons/lang3/text/FormattableUtilsTest.java new file mode 100644 index 0000000..8ed9ea4 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/text/FormattableUtilsTest.java @@ -0,0 +1,117 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.text; + +import static java.util.FormattableFlags.LEFT_JUSTIFY; +import static org.junit.Assert.assertEquals; + +import java.util.Formatter; + +import org.junit.Test; + +/** + * Unit tests {@link FormattableUtils}. + * + * @version $Id: FormattableUtilsTest.java 1127546 2011-05-25 14:44:04Z scolebourne $ + */ +public class FormattableUtilsTest { + + @Test + public void testDefaultAppend() { + assertEquals("foo", FormattableUtils.append("foo", new Formatter(), 0, -1, -1).toString()); + assertEquals("fo", FormattableUtils.append("foo", new Formatter(), 0, -1, 2).toString()); + assertEquals(" foo", FormattableUtils.append("foo", new Formatter(), 0, 4, -1).toString()); + assertEquals(" foo", FormattableUtils.append("foo", new Formatter(), 0, 6, -1).toString()); + assertEquals(" fo", FormattableUtils.append("foo", new Formatter(), 0, 3, 2).toString()); + assertEquals(" fo", FormattableUtils.append("foo", new Formatter(), 0, 5, 2).toString()); + assertEquals("foo ", FormattableUtils.append("foo", new Formatter(), LEFT_JUSTIFY, 4, -1).toString()); + assertEquals("foo ", FormattableUtils.append("foo", new Formatter(), LEFT_JUSTIFY, 6, -1).toString()); + assertEquals("fo ", FormattableUtils.append("foo", new Formatter(), LEFT_JUSTIFY, 3, 2).toString()); + assertEquals("fo ", FormattableUtils.append("foo", new Formatter(), LEFT_JUSTIFY, 5, 2).toString()); + } + + @Test + public void testAlternatePadCharacter() { + char pad='_'; + assertEquals("foo", FormattableUtils.append("foo", new Formatter(), 0, -1, -1, pad).toString()); + assertEquals("fo", FormattableUtils.append("foo", new Formatter(), 0, -1, 2, pad).toString()); + assertEquals("_foo", FormattableUtils.append("foo", new Formatter(), 0, 4, -1, pad).toString()); + assertEquals("___foo", FormattableUtils.append("foo", new Formatter(), 0, 6, -1, pad).toString()); + assertEquals("_fo", FormattableUtils.append("foo", new Formatter(), 0, 3, 2, pad).toString()); + assertEquals("___fo", FormattableUtils.append("foo", new Formatter(), 0, 5, 2, pad).toString()); + assertEquals("foo_", FormattableUtils.append("foo", new Formatter(), LEFT_JUSTIFY, 4, -1, pad).toString()); + assertEquals("foo___", FormattableUtils.append("foo", new Formatter(), LEFT_JUSTIFY, 6, -1, pad).toString()); + assertEquals("fo_", FormattableUtils.append("foo", new Formatter(), LEFT_JUSTIFY, 3, 2, pad).toString()); + assertEquals("fo___", FormattableUtils.append("foo", new Formatter(), LEFT_JUSTIFY, 5, 2, pad).toString()); + } + + @Test + public void testEllipsis() { + assertEquals("foo", FormattableUtils.append("foo", new Formatter(), 0, -1, -1, "*").toString()); + assertEquals("f*", FormattableUtils.append("foo", new Formatter(), 0, -1, 2, "*").toString()); + assertEquals(" foo", FormattableUtils.append("foo", new Formatter(), 0, 4, -1, "*").toString()); + assertEquals(" foo", FormattableUtils.append("foo", new Formatter(), 0, 6, -1, "*").toString()); + assertEquals(" f*", FormattableUtils.append("foo", new Formatter(), 0, 3, 2, "*").toString()); + assertEquals(" f*", FormattableUtils.append("foo", new Formatter(), 0, 5, 2, "*").toString()); + assertEquals("foo ", FormattableUtils.append("foo", new Formatter(), LEFT_JUSTIFY, 4, -1, "*").toString()); + assertEquals("foo ", FormattableUtils.append("foo", new Formatter(), LEFT_JUSTIFY, 6, -1, "*").toString()); + assertEquals("f* ", FormattableUtils.append("foo", new Formatter(), LEFT_JUSTIFY, 3, 2, "*").toString()); + assertEquals("f* ", FormattableUtils.append("foo", new Formatter(), LEFT_JUSTIFY, 5, 2, "*").toString()); + + assertEquals("foo", FormattableUtils.append("foo", new Formatter(), 0, -1, -1, "+*").toString()); + assertEquals("+*", FormattableUtils.append("foo", new Formatter(), 0, -1, 2, "+*").toString()); + assertEquals(" foo", FormattableUtils.append("foo", new Formatter(), 0, 4, -1, "+*").toString()); + assertEquals(" foo", FormattableUtils.append("foo", new Formatter(), 0, 6, -1, "+*").toString()); + assertEquals(" +*", FormattableUtils.append("foo", new Formatter(), 0, 3, 2, "+*").toString()); + assertEquals(" +*", FormattableUtils.append("foo", new Formatter(), 0, 5, 2, "+*").toString()); + assertEquals("foo ", FormattableUtils.append("foo", new Formatter(), LEFT_JUSTIFY, 4, -1, "+*").toString()); + assertEquals("foo ", FormattableUtils.append("foo", new Formatter(), LEFT_JUSTIFY, 6, -1, "+*").toString()); + assertEquals("+* ", FormattableUtils.append("foo", new Formatter(), LEFT_JUSTIFY, 3, 2, "+*").toString()); + assertEquals("+* ", FormattableUtils.append("foo", new Formatter(), LEFT_JUSTIFY, 5, 2, "+*").toString()); + } + + @Test(expected=IllegalArgumentException.class) + public void testIllegalEllipsis() { + FormattableUtils.append("foo", new Formatter(), 0, -1, 1, "xx"); + } + + @Test + public void testAlternatePadCharAndEllipsis() { + assertEquals("foo", FormattableUtils.append("foo", new Formatter(), 0, -1, -1, '_', "*").toString()); + assertEquals("f*", FormattableUtils.append("foo", new Formatter(), 0, -1, 2, '_', "*").toString()); + assertEquals("_foo", FormattableUtils.append("foo", new Formatter(), 0, 4, -1, '_', "*").toString()); + assertEquals("___foo", FormattableUtils.append("foo", new Formatter(), 0, 6, -1, '_', "*").toString()); + assertEquals("_f*", FormattableUtils.append("foo", new Formatter(), 0, 3, 2, '_', "*").toString()); + assertEquals("___f*", FormattableUtils.append("foo", new Formatter(), 0, 5, 2, '_', "*").toString()); + assertEquals("foo_", FormattableUtils.append("foo", new Formatter(), LEFT_JUSTIFY, 4, -1, '_', "*").toString()); + assertEquals("foo___", FormattableUtils.append("foo", new Formatter(), LEFT_JUSTIFY, 6, -1, '_', "*").toString()); + assertEquals("f*_", FormattableUtils.append("foo", new Formatter(), LEFT_JUSTIFY, 3, 2, '_', "*").toString()); + assertEquals("f*___", FormattableUtils.append("foo", new Formatter(), LEFT_JUSTIFY, 5, 2, '_', "*").toString()); + + assertEquals("foo", FormattableUtils.append("foo", new Formatter(), 0, -1, -1, '_', "+*").toString()); + assertEquals("+*", FormattableUtils.append("foo", new Formatter(), 0, -1, 2, '_', "+*").toString()); + assertEquals("_foo", FormattableUtils.append("foo", new Formatter(), 0, 4, -1, '_', "+*").toString()); + assertEquals("___foo", FormattableUtils.append("foo", new Formatter(), 0, 6, -1, '_', "+*").toString()); + assertEquals("_+*", FormattableUtils.append("foo", new Formatter(), 0, 3, 2, '_', "+*").toString()); + assertEquals("___+*", FormattableUtils.append("foo", new Formatter(), 0, 5, 2, '_', "+*").toString()); + assertEquals("foo_", FormattableUtils.append("foo", new Formatter(), LEFT_JUSTIFY, 4, -1, '_', "+*").toString()); + assertEquals("foo___", FormattableUtils.append("foo", new Formatter(), LEFT_JUSTIFY, 6, -1, '_', "+*").toString()); + assertEquals("+*_", FormattableUtils.append("foo", new Formatter(), LEFT_JUSTIFY, 3, 2, '_', "+*").toString()); + assertEquals("+*___", FormattableUtils.append("foo", new Formatter(), LEFT_JUSTIFY, 5, 2, '_', "+*").toString()); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/text/StrBuilder.java b/ApacheCommonsLang/org/apache/commons/lang3/text/StrBuilder.java new file mode 100644 index 0000000..842cc70 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/text/StrBuilder.java @@ -0,0 +1,2856 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.text; + +import java.io.Reader; +import java.io.Serializable; +import java.io.Writer; +import java.util.Iterator; +import java.util.List; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.SystemUtils; + +/** + * Builds a string from constituent parts providing a more flexible and powerful API + * than StringBuffer. + *

+ * The main differences from StringBuffer/StringBuilder are: + *

    + *
  • Not synchronized
  • + *
  • Not final
  • + *
  • Subclasses have direct access to character array
  • + *
  • Additional methods + *
      + *
    • appendWithSeparators - adds an array of values, with a separator
    • + *
    • appendPadding - adds a length padding characters
    • + *
    • appendFixedLength - adds a fixed width field to the builder
    • + *
    • toCharArray/getChars - simpler ways to get a range of the character array
    • + *
    • delete - delete char or string
    • + *
    • replace - search and replace for a char or string
    • + *
    • leftString/rightString/midString - substring without exceptions
    • + *
    • contains - whether the builder contains a char or string
    • + *
    • size/clear/isEmpty - collections style API methods
    • + *
    + *
  • + *
+ *
  • Views + *
      + *
    • asTokenizer - uses the internal buffer as the source of a StrTokenizer
    • + *
    • asReader - uses the internal buffer as the source of a Reader
    • + *
    • asWriter - allows a Writer to write directly to the internal buffer
    • + *
    + *
  • + * + *

    + * The aim has been to provide an API that mimics very closely what StringBuffer + * provides, but with additional methods. It should be noted that some edge cases, + * with invalid indices or null input, have been altered - see individual methods. + * The biggest of these changes is that by default, null will not output the text + * 'null'. This can be controlled by a property, {@link #setNullText(String)}. + *

    + * Prior to 3.0, this class implemented Cloneable but did not implement the + * clone method so could not be used. From 3.0 onwards it no longer implements + * the interface. + * + * @since 2.2 + * @version $Id: StrBuilder.java 1309977 2012-04-05 17:53:39Z ggregory $ + */ +public class StrBuilder implements CharSequence, Appendable, Serializable { + + /** + * The extra capacity for new builders. + */ + static final int CAPACITY = 32; + + /** + * Required for serialization support. + * + * @see java.io.Serializable + */ + private static final long serialVersionUID = 7628716375283629643L; + + /** Internal data storage. */ + protected char[] buffer; // TODO make private? + /** Current size of the buffer. */ + protected int size; // TODO make private? + /** The new line. */ + private String newLine; + /** The null text. */ + private String nullText; + + //----------------------------------------------------------------------- + /** + * Constructor that creates an empty builder initial capacity 32 characters. + */ + public StrBuilder() { + this(CAPACITY); + } + + /** + * Constructor that creates an empty builder the specified initial capacity. + * + * @param initialCapacity the initial capacity, zero or less will be converted to 32 + */ + public StrBuilder(int initialCapacity) { + super(); + if (initialCapacity <= 0) { + initialCapacity = CAPACITY; + } + buffer = new char[initialCapacity]; + } + + /** + * Constructor that creates a builder from the string, allocating + * 32 extra characters for growth. + * + * @param str the string to copy, null treated as blank string + */ + public StrBuilder(String str) { + super(); + if (str == null) { + buffer = new char[CAPACITY]; + } else { + buffer = new char[str.length() + CAPACITY]; + append(str); + } + } + + //----------------------------------------------------------------------- + /** + * Gets the text to be appended when a new line is added. + * + * @return the new line text, null means use system default + */ + public String getNewLineText() { + return newLine; + } + + /** + * Sets the text to be appended when a new line is added. + * + * @param newLine the new line text, null means use system default + * @return this, to enable chaining + */ + public StrBuilder setNewLineText(String newLine) { + this.newLine = newLine; + return this; + } + + //----------------------------------------------------------------------- + /** + * Gets the text to be appended when null is added. + * + * @return the null text, null means no append + */ + public String getNullText() { + return nullText; + } + + /** + * Sets the text to be appended when null is added. + * + * @param nullText the null text, null means no append + * @return this, to enable chaining + */ + public StrBuilder setNullText(String nullText) { + if (nullText != null && nullText.length() == 0) { + nullText = null; + } + this.nullText = nullText; + return this; + } + + //----------------------------------------------------------------------- + /** + * Gets the length of the string builder. + * + * @return the length + */ + @Override + public int length() { + return size; + } + + /** + * Updates the length of the builder by either dropping the last characters + * or adding filler of Unicode zero. + * + * @param length the length to set to, must be zero or positive + * @return this, to enable chaining + * @throws IndexOutOfBoundsException if the length is negative + */ + public StrBuilder setLength(int length) { + if (length < 0) { + throw new StringIndexOutOfBoundsException(length); + } + if (length < size) { + size = length; + } else if (length > size) { + ensureCapacity(length); + int oldEnd = size; + int newEnd = length; + size = length; + for (int i = oldEnd; i < newEnd; i++) { + buffer[i] = '\0'; + } + } + return this; + } + + //----------------------------------------------------------------------- + /** + * Gets the current size of the internal character array buffer. + * + * @return the capacity + */ + public int capacity() { + return buffer.length; + } + + /** + * Checks the capacity and ensures that it is at least the size specified. + * + * @param capacity the capacity to ensure + * @return this, to enable chaining + */ + public StrBuilder ensureCapacity(int capacity) { + if (capacity > buffer.length) { + char[] old = buffer; + buffer = new char[capacity * 2]; + System.arraycopy(old, 0, buffer, 0, size); + } + return this; + } + + /** + * Minimizes the capacity to the actual length of the string. + * + * @return this, to enable chaining + */ + public StrBuilder minimizeCapacity() { + if (buffer.length > length()) { + char[] old = buffer; + buffer = new char[length()]; + System.arraycopy(old, 0, buffer, 0, size); + } + return this; + } + + //----------------------------------------------------------------------- + /** + * Gets the length of the string builder. + *

    + * This method is the same as {@link #length()} and is provided to match the + * API of Collections. + * + * @return the length + */ + public int size() { + return size; + } + + /** + * Checks is the string builder is empty (convenience Collections API style method). + *

    + * This method is the same as checking {@link #length()} and is provided to match the + * API of Collections. + * + * @return true if the size is 0. + */ + public boolean isEmpty() { + return size == 0; + } + + /** + * Clears the string builder (convenience Collections API style method). + *

    + * This method does not reduce the size of the internal character buffer. + * To do that, call clear() followed by {@link #minimizeCapacity()}. + *

    + * This method is the same as {@link #setLength(int)} called with zero + * and is provided to match the API of Collections. + * + * @return this, to enable chaining + */ + public StrBuilder clear() { + size = 0; + return this; + } + + //----------------------------------------------------------------------- + /** + * Gets the character at the specified index. + * + * @see #setCharAt(int, char) + * @see #deleteCharAt(int) + * @param index the index to retrieve, must be valid + * @return the character at the index + * @throws IndexOutOfBoundsException if the index is invalid + */ + @Override + public char charAt(int index) { + if (index < 0 || index >= length()) { + throw new StringIndexOutOfBoundsException(index); + } + return buffer[index]; + } + + /** + * Sets the character at the specified index. + * + * @see #charAt(int) + * @see #deleteCharAt(int) + * @param index the index to set + * @param ch the new character + * @return this, to enable chaining + * @throws IndexOutOfBoundsException if the index is invalid + */ + public StrBuilder setCharAt(int index, char ch) { + if (index < 0 || index >= length()) { + throw new StringIndexOutOfBoundsException(index); + } + buffer[index] = ch; + return this; + } + + /** + * Deletes the character at the specified index. + * + * @see #charAt(int) + * @see #setCharAt(int, char) + * @param index the index to delete + * @return this, to enable chaining + * @throws IndexOutOfBoundsException if the index is invalid + */ + public StrBuilder deleteCharAt(int index) { + if (index < 0 || index >= size) { + throw new StringIndexOutOfBoundsException(index); + } + deleteImpl(index, index + 1, 1); + return this; + } + + //----------------------------------------------------------------------- + /** + * Copies the builder's character array into a new character array. + * + * @return a new array that represents the contents of the builder + */ + public char[] toCharArray() { + if (size == 0) { + return ArrayUtils.EMPTY_CHAR_ARRAY; + } + char chars[] = new char[size]; + System.arraycopy(buffer, 0, chars, 0, size); + return chars; + } + + /** + * Copies part of the builder's character array into a new character array. + * + * @param startIndex the start index, inclusive, must be valid + * @param endIndex the end index, exclusive, must be valid except that + * if too large it is treated as end of string + * @return a new array that holds part of the contents of the builder + * @throws IndexOutOfBoundsException if startIndex is invalid, + * or if endIndex is invalid (but endIndex greater than size is valid) + */ + public char[] toCharArray(int startIndex, int endIndex) { + endIndex = validateRange(startIndex, endIndex); + int len = endIndex - startIndex; + if (len == 0) { + return ArrayUtils.EMPTY_CHAR_ARRAY; + } + char chars[] = new char[len]; + System.arraycopy(buffer, startIndex, chars, 0, len); + return chars; + } + + /** + * Copies the character array into the specified array. + * + * @param destination the destination array, null will cause an array to be created + * @return the input array, unless that was null or too small + */ + public char[] getChars(char[] destination) { + int len = length(); + if (destination == null || destination.length < len) { + destination = new char[len]; + } + System.arraycopy(buffer, 0, destination, 0, len); + return destination; + } + + /** + * Copies the character array into the specified array. + * + * @param startIndex first index to copy, inclusive, must be valid + * @param endIndex last index, exclusive, must be valid + * @param destination the destination array, must not be null or too small + * @param destinationIndex the index to start copying in destination + * @throws NullPointerException if the array is null + * @throws IndexOutOfBoundsException if any index is invalid + */ + public void getChars(int startIndex, int endIndex, char destination[], int destinationIndex) { + if (startIndex < 0) { + throw new StringIndexOutOfBoundsException(startIndex); + } + if (endIndex < 0 || endIndex > length()) { + throw new StringIndexOutOfBoundsException(endIndex); + } + if (startIndex > endIndex) { + throw new StringIndexOutOfBoundsException("end < start"); + } + System.arraycopy(buffer, startIndex, destination, destinationIndex, endIndex - startIndex); + } + + //----------------------------------------------------------------------- + /** + * Appends the new line string to this string builder. + *

    + * The new line string can be altered using {@link #setNewLineText(String)}. + * This might be used to force the output to always use Unix line endings + * even when on Windows. + * + * @return this, to enable chaining + */ + public StrBuilder appendNewLine() { + if (newLine == null) { + append(SystemUtils.LINE_SEPARATOR); + return this; + } + return append(newLine); + } + + /** + * Appends the text representing null to this string builder. + * + * @return this, to enable chaining + */ + public StrBuilder appendNull() { + if (nullText == null) { + return this; + } + return append(nullText); + } + + /** + * Appends an object to this string builder. + * Appending null will call {@link #appendNull()}. + * + * @param obj the object to append + * @return this, to enable chaining + */ + public StrBuilder append(Object obj) { + if (obj == null) { + return appendNull(); + } + return append(obj.toString()); + } + + /** + * Appends a CharSequence to this string builder. + * Appending null will call {@link #appendNull()}. + * + * @param seq the CharSequence to append + * @return this, to enable chaining + * @since 3.0 + */ + @Override + public StrBuilder append(CharSequence seq) { + if (seq == null) { + return appendNull(); + } + return append(seq.toString()); + } + + /** + * Appends part of a CharSequence to this string builder. + * Appending null will call {@link #appendNull()}. + * + * @param seq the CharSequence to append + * @param startIndex the start index, inclusive, must be valid + * @param length the length to append, must be valid + * @return this, to enable chaining + * @since 3.0 + */ + @Override + public StrBuilder append(CharSequence seq, int startIndex, int length) { + if (seq == null) { + return appendNull(); + } + return append(seq.toString(), startIndex, length); + } + + /** + * Appends a string to this string builder. + * Appending null will call {@link #appendNull()}. + * + * @param str the string to append + * @return this, to enable chaining + */ + public StrBuilder append(String str) { + if (str == null) { + return appendNull(); + } + int strLen = str.length(); + if (strLen > 0) { + int len = length(); + ensureCapacity(len + strLen); + str.getChars(0, strLen, buffer, len); + size += strLen; + } + return this; + } + + /** + * Appends part of a string to this string builder. + * Appending null will call {@link #appendNull()}. + * + * @param str the string to append + * @param startIndex the start index, inclusive, must be valid + * @param length the length to append, must be valid + * @return this, to enable chaining + */ + public StrBuilder append(String str, int startIndex, int length) { + if (str == null) { + return appendNull(); + } + if (startIndex < 0 || startIndex > str.length()) { + throw new StringIndexOutOfBoundsException("startIndex must be valid"); + } + if (length < 0 || (startIndex + length) > str.length()) { + throw new StringIndexOutOfBoundsException("length must be valid"); + } + if (length > 0) { + int len = length(); + ensureCapacity(len + length); + str.getChars(startIndex, startIndex + length, buffer, len); + size += length; + } + return this; + } + + /** + * Appends a string buffer to this string builder. + * Appending null will call {@link #appendNull()}. + * + * @param str the string buffer to append + * @return this, to enable chaining + */ + public StrBuilder append(StringBuffer str) { + if (str == null) { + return appendNull(); + } + int strLen = str.length(); + if (strLen > 0) { + int len = length(); + ensureCapacity(len + strLen); + str.getChars(0, strLen, buffer, len); + size += strLen; + } + return this; + } + + /** + * Appends part of a string buffer to this string builder. + * Appending null will call {@link #appendNull()}. + * + * @param str the string to append + * @param startIndex the start index, inclusive, must be valid + * @param length the length to append, must be valid + * @return this, to enable chaining + */ + public StrBuilder append(StringBuffer str, int startIndex, int length) { + if (str == null) { + return appendNull(); + } + if (startIndex < 0 || startIndex > str.length()) { + throw new StringIndexOutOfBoundsException("startIndex must be valid"); + } + if (length < 0 || (startIndex + length) > str.length()) { + throw new StringIndexOutOfBoundsException("length must be valid"); + } + if (length > 0) { + int len = length(); + ensureCapacity(len + length); + str.getChars(startIndex, startIndex + length, buffer, len); + size += length; + } + return this; + } + + /** + * Appends another string builder to this string builder. + * Appending null will call {@link #appendNull()}. + * + * @param str the string builder to append + * @return this, to enable chaining + */ + public StrBuilder append(StrBuilder str) { + if (str == null) { + return appendNull(); + } + int strLen = str.length(); + if (strLen > 0) { + int len = length(); + ensureCapacity(len + strLen); + System.arraycopy(str.buffer, 0, buffer, len, strLen); + size += strLen; + } + return this; + } + + /** + * Appends part of a string builder to this string builder. + * Appending null will call {@link #appendNull()}. + * + * @param str the string to append + * @param startIndex the start index, inclusive, must be valid + * @param length the length to append, must be valid + * @return this, to enable chaining + */ + public StrBuilder append(StrBuilder str, int startIndex, int length) { + if (str == null) { + return appendNull(); + } + if (startIndex < 0 || startIndex > str.length()) { + throw new StringIndexOutOfBoundsException("startIndex must be valid"); + } + if (length < 0 || (startIndex + length) > str.length()) { + throw new StringIndexOutOfBoundsException("length must be valid"); + } + if (length > 0) { + int len = length(); + ensureCapacity(len + length); + str.getChars(startIndex, startIndex + length, buffer, len); + size += length; + } + return this; + } + + /** + * Appends a char array to the string builder. + * Appending null will call {@link #appendNull()}. + * + * @param chars the char array to append + * @return this, to enable chaining + */ + public StrBuilder append(char[] chars) { + if (chars == null) { + return appendNull(); + } + int strLen = chars.length; + if (strLen > 0) { + int len = length(); + ensureCapacity(len + strLen); + System.arraycopy(chars, 0, buffer, len, strLen); + size += strLen; + } + return this; + } + + /** + * Appends a char array to the string builder. + * Appending null will call {@link #appendNull()}. + * + * @param chars the char array to append + * @param startIndex the start index, inclusive, must be valid + * @param length the length to append, must be valid + * @return this, to enable chaining + */ + public StrBuilder append(char[] chars, int startIndex, int length) { + if (chars == null) { + return appendNull(); + } + if (startIndex < 0 || startIndex > chars.length) { + throw new StringIndexOutOfBoundsException("Invalid startIndex: " + length); + } + if (length < 0 || (startIndex + length) > chars.length) { + throw new StringIndexOutOfBoundsException("Invalid length: " + length); + } + if (length > 0) { + int len = length(); + ensureCapacity(len + length); + System.arraycopy(chars, startIndex, buffer, len, length); + size += length; + } + return this; + } + + /** + * Appends a boolean value to the string builder. + * + * @param value the value to append + * @return this, to enable chaining + */ + public StrBuilder append(boolean value) { + if (value) { + ensureCapacity(size + 4); + buffer[size++] = 't'; + buffer[size++] = 'r'; + buffer[size++] = 'u'; + buffer[size++] = 'e'; + } else { + ensureCapacity(size + 5); + buffer[size++] = 'f'; + buffer[size++] = 'a'; + buffer[size++] = 'l'; + buffer[size++] = 's'; + buffer[size++] = 'e'; + } + return this; + } + + /** + * Appends a char value to the string builder. + * + * @param ch the value to append + * @return this, to enable chaining + * @since 3.0 + */ + @Override + public StrBuilder append(char ch) { + int len = length(); + ensureCapacity(len + 1); + buffer[size++] = ch; + return this; + } + + /** + * Appends an int value to the string builder using String.valueOf. + * + * @param value the value to append + * @return this, to enable chaining + */ + public StrBuilder append(int value) { + return append(String.valueOf(value)); + } + + /** + * Appends a long value to the string builder using String.valueOf. + * + * @param value the value to append + * @return this, to enable chaining + */ + public StrBuilder append(long value) { + return append(String.valueOf(value)); + } + + /** + * Appends a float value to the string builder using String.valueOf. + * + * @param value the value to append + * @return this, to enable chaining + */ + public StrBuilder append(float value) { + return append(String.valueOf(value)); + } + + /** + * Appends a double value to the string builder using String.valueOf. + * + * @param value the value to append + * @return this, to enable chaining + */ + public StrBuilder append(double value) { + return append(String.valueOf(value)); + } + + //----------------------------------------------------------------------- + /** + * Appends an object followed by a new line to this string builder. + * Appending null will call {@link #appendNull()}. + * + * @param obj the object to append + * @return this, to enable chaining + * @since 2.3 + */ + public StrBuilder appendln(Object obj) { + return append(obj).appendNewLine(); + } + + /** + * Appends a string followed by a new line to this string builder. + * Appending null will call {@link #appendNull()}. + * + * @param str the string to append + * @return this, to enable chaining + * @since 2.3 + */ + public StrBuilder appendln(String str) { + return append(str).appendNewLine(); + } + + /** + * Appends part of a string followed by a new line to this string builder. + * Appending null will call {@link #appendNull()}. + * + * @param str the string to append + * @param startIndex the start index, inclusive, must be valid + * @param length the length to append, must be valid + * @return this, to enable chaining + * @since 2.3 + */ + public StrBuilder appendln(String str, int startIndex, int length) { + return append(str, startIndex, length).appendNewLine(); + } + + /** + * Appends a string buffer followed by a new line to this string builder. + * Appending null will call {@link #appendNull()}. + * + * @param str the string buffer to append + * @return this, to enable chaining + * @since 2.3 + */ + public StrBuilder appendln(StringBuffer str) { + return append(str).appendNewLine(); + } + + /** + * Appends part of a string buffer followed by a new line to this string builder. + * Appending null will call {@link #appendNull()}. + * + * @param str the string to append + * @param startIndex the start index, inclusive, must be valid + * @param length the length to append, must be valid + * @return this, to enable chaining + * @since 2.3 + */ + public StrBuilder appendln(StringBuffer str, int startIndex, int length) { + return append(str, startIndex, length).appendNewLine(); + } + + /** + * Appends another string builder followed by a new line to this string builder. + * Appending null will call {@link #appendNull()}. + * + * @param str the string builder to append + * @return this, to enable chaining + * @since 2.3 + */ + public StrBuilder appendln(StrBuilder str) { + return append(str).appendNewLine(); + } + + /** + * Appends part of a string builder followed by a new line to this string builder. + * Appending null will call {@link #appendNull()}. + * + * @param str the string to append + * @param startIndex the start index, inclusive, must be valid + * @param length the length to append, must be valid + * @return this, to enable chaining + * @since 2.3 + */ + public StrBuilder appendln(StrBuilder str, int startIndex, int length) { + return append(str, startIndex, length).appendNewLine(); + } + + /** + * Appends a char array followed by a new line to the string builder. + * Appending null will call {@link #appendNull()}. + * + * @param chars the char array to append + * @return this, to enable chaining + * @since 2.3 + */ + public StrBuilder appendln(char[] chars) { + return append(chars).appendNewLine(); + } + + /** + * Appends a char array followed by a new line to the string builder. + * Appending null will call {@link #appendNull()}. + * + * @param chars the char array to append + * @param startIndex the start index, inclusive, must be valid + * @param length the length to append, must be valid + * @return this, to enable chaining + * @since 2.3 + */ + public StrBuilder appendln(char[] chars, int startIndex, int length) { + return append(chars, startIndex, length).appendNewLine(); + } + + /** + * Appends a boolean value followed by a new line to the string builder. + * + * @param value the value to append + * @return this, to enable chaining + * @since 2.3 + */ + public StrBuilder appendln(boolean value) { + return append(value).appendNewLine(); + } + + /** + * Appends a char value followed by a new line to the string builder. + * + * @param ch the value to append + * @return this, to enable chaining + * @since 2.3 + */ + public StrBuilder appendln(char ch) { + return append(ch).appendNewLine(); + } + + /** + * Appends an int value followed by a new line to the string builder using String.valueOf. + * + * @param value the value to append + * @return this, to enable chaining + * @since 2.3 + */ + public StrBuilder appendln(int value) { + return append(value).appendNewLine(); + } + + /** + * Appends a long value followed by a new line to the string builder using String.valueOf. + * + * @param value the value to append + * @return this, to enable chaining + * @since 2.3 + */ + public StrBuilder appendln(long value) { + return append(value).appendNewLine(); + } + + /** + * Appends a float value followed by a new line to the string builder using String.valueOf. + * + * @param value the value to append + * @return this, to enable chaining + * @since 2.3 + */ + public StrBuilder appendln(float value) { + return append(value).appendNewLine(); + } + + /** + * Appends a double value followed by a new line to the string builder using String.valueOf. + * + * @param value the value to append + * @return this, to enable chaining + * @since 2.3 + */ + public StrBuilder appendln(double value) { + return append(value).appendNewLine(); + } + + //----------------------------------------------------------------------- + /** + * Appends each item in an array to the builder without any separators. + * Appending a null array will have no effect. + * Each object is appended using {@link #append(Object)}. + * + * @param array the array to append + * @return this, to enable chaining + * @since 2.3 + */ + public StrBuilder appendAll(Object[] array) { + if (array != null && array.length > 0) { + for (Object element : array) { + append(element); + } + } + return this; + } + + /** + * Appends each item in a iterable to the builder without any separators. + * Appending a null iterable will have no effect. + * Each object is appended using {@link #append(Object)}. + * + * @param iterable the iterable to append + * @return this, to enable chaining + * @since 2.3 + */ + public StrBuilder appendAll(Iterable iterable) { + if (iterable != null) { + Iterator it = iterable.iterator(); + while (it.hasNext()) { + append(it.next()); + } + } + return this; + } + + /** + * Appends each item in an iterator to the builder without any separators. + * Appending a null iterator will have no effect. + * Each object is appended using {@link #append(Object)}. + * + * @param it the iterator to append + * @return this, to enable chaining + * @since 2.3 + */ + public StrBuilder appendAll(Iterator it) { + if (it != null) { + while (it.hasNext()) { + append(it.next()); + } + } + return this; + } + + //----------------------------------------------------------------------- + /** + * Appends an array placing separators between each value, but + * not before the first or after the last. + * Appending a null array will have no effect. + * Each object is appended using {@link #append(Object)}. + * + * @param array the array to append + * @param separator the separator to use, null means no separator + * @return this, to enable chaining + */ + public StrBuilder appendWithSeparators(Object[] array, String separator) { + if (array != null && array.length > 0) { + separator = ObjectUtils.toString(separator); + append(array[0]); + for (int i = 1; i < array.length; i++) { + append(separator); + append(array[i]); + } + } + return this; + } + + /** + * Appends a iterable placing separators between each value, but + * not before the first or after the last. + * Appending a null iterable will have no effect. + * Each object is appended using {@link #append(Object)}. + * + * @param iterable the iterable to append + * @param separator the separator to use, null means no separator + * @return this, to enable chaining + */ + public StrBuilder appendWithSeparators(Iterable iterable, String separator) { + if (iterable != null) { + separator = ObjectUtils.toString(separator); + Iterator it = iterable.iterator(); + while (it.hasNext()) { + append(it.next()); + if (it.hasNext()) { + append(separator); + } + } + } + return this; + } + + /** + * Appends an iterator placing separators between each value, but + * not before the first or after the last. + * Appending a null iterator will have no effect. + * Each object is appended using {@link #append(Object)}. + * + * @param it the iterator to append + * @param separator the separator to use, null means no separator + * @return this, to enable chaining + */ + public StrBuilder appendWithSeparators(Iterator it, String separator) { + if (it != null) { + separator = ObjectUtils.toString(separator); + while (it.hasNext()) { + append(it.next()); + if (it.hasNext()) { + append(separator); + } + } + } + return this; + } + + //----------------------------------------------------------------------- + /** + * Appends a separator if the builder is currently non-empty. + * Appending a null separator will have no effect. + * The separator is appended using {@link #append(String)}. + *

    + * This method is useful for adding a separator each time around the + * loop except the first. + *

    +     * for (Iterator it = list.iterator(); it.hasNext(); ) {
    +     *   appendSeparator(",");
    +     *   append(it.next());
    +     * }
    +     * 
    + * Note that for this simple example, you should use + * {@link #appendWithSeparators(Iterable, String)}. + * + * @param separator the separator to use, null means no separator + * @return this, to enable chaining + * @since 2.3 + */ + public StrBuilder appendSeparator(String separator) { + return appendSeparator(separator, null); + } + + /** + * Appends one of both separators to the StrBuilder. + * If the builder is currently empty it will append the defaultIfEmpty-separator + * Otherwise it will append the standard-separator + * + * Appending a null separator will have no effect. + * The separator is appended using {@link #append(String)}. + *

    + * This method is for example useful for constructing queries + *

    +     * StrBuilder whereClause = new StrBuilder();
    +     * if(searchCommand.getPriority() != null) {
    +     *  whereClause.appendSeparator(" and", " where");
    +     *  whereClause.append(" priority = ?")
    +     * }
    +     * if(searchCommand.getComponent() != null) {
    +     *  whereClause.appendSeparator(" and", " where");
    +     *  whereClause.append(" component = ?")
    +     * }
    +     * selectClause.append(whereClause)
    +     * 
    + * + * @param standard the separator if builder is not empty, null means no separator + * @param defaultIfEmpty the separator if builder is empty, null means no separator + * @return this, to enable chaining + * @since 2.5 + */ + public StrBuilder appendSeparator(String standard, String defaultIfEmpty) { + String str = isEmpty() ? defaultIfEmpty : standard; + if (str != null) { + append(str); + } + return this; + } + + /** + * Appends a separator if the builder is currently non-empty. + * The separator is appended using {@link #append(char)}. + *

    + * This method is useful for adding a separator each time around the + * loop except the first. + *

    +     * for (Iterator it = list.iterator(); it.hasNext(); ) {
    +     *   appendSeparator(',');
    +     *   append(it.next());
    +     * }
    +     * 
    + * Note that for this simple example, you should use + * {@link #appendWithSeparators(Iterable, String)}. + * + * @param separator the separator to use + * @return this, to enable chaining + * @since 2.3 + */ + public StrBuilder appendSeparator(char separator) { + if (size() > 0) { + append(separator); + } + return this; + } + + /** + * Append one of both separators to the builder + * If the builder is currently empty it will append the defaultIfEmpty-separator + * Otherwise it will append the standard-separator + * + * The separator is appended using {@link #append(char)}. + * @param standard the separator if builder is not empty + * @param defaultIfEmpty the separator if builder is empty + * @return this, to enable chaining + * @since 2.5 + */ + public StrBuilder appendSeparator(char standard, char defaultIfEmpty) { + if (size() > 0) { + append(standard); + } else { + append(defaultIfEmpty); + } + return this; + } + /** + * Appends a separator to the builder if the loop index is greater than zero. + * Appending a null separator will have no effect. + * The separator is appended using {@link #append(String)}. + *

    + * This method is useful for adding a separator each time around the + * loop except the first. + *

    +     * for (int i = 0; i < list.size(); i++) {
    +     *   appendSeparator(",", i);
    +     *   append(list.get(i));
    +     * }
    +     * 
    + * Note that for this simple example, you should use + * {@link #appendWithSeparators(Iterable, String)}. + * + * @param separator the separator to use, null means no separator + * @param loopIndex the loop index + * @return this, to enable chaining + * @since 2.3 + */ + public StrBuilder appendSeparator(String separator, int loopIndex) { + if (separator != null && loopIndex > 0) { + append(separator); + } + return this; + } + + /** + * Appends a separator to the builder if the loop index is greater than zero. + * The separator is appended using {@link #append(char)}. + *

    + * This method is useful for adding a separator each time around the + * loop except the first. + *

    +     * for (int i = 0; i < list.size(); i++) {
    +     *   appendSeparator(",", i);
    +     *   append(list.get(i));
    +     * }
    +     * 
    + * Note that for this simple example, you should use + * {@link #appendWithSeparators(Iterable, String)}. + * + * @param separator the separator to use + * @param loopIndex the loop index + * @return this, to enable chaining + * @since 2.3 + */ + public StrBuilder appendSeparator(char separator, int loopIndex) { + if (loopIndex > 0) { + append(separator); + } + return this; + } + + //----------------------------------------------------------------------- + /** + * Appends the pad character to the builder the specified number of times. + * + * @param length the length to append, negative means no append + * @param padChar the character to append + * @return this, to enable chaining + */ + public StrBuilder appendPadding(int length, char padChar) { + if (length >= 0) { + ensureCapacity(size + length); + for (int i = 0; i < length; i++) { + buffer[size++] = padChar; + } + } + return this; + } + + //----------------------------------------------------------------------- + /** + * Appends an object to the builder padding on the left to a fixed width. + * The toString of the object is used. + * If the object is larger than the length, the left hand side is lost. + * If the object is null, the null text value is used. + * + * @param obj the object to append, null uses null text + * @param width the fixed field width, zero or negative has no effect + * @param padChar the pad character to use + * @return this, to enable chaining + */ + public StrBuilder appendFixedWidthPadLeft(Object obj, int width, char padChar) { + if (width > 0) { + ensureCapacity(size + width); + String str = (obj == null ? getNullText() : obj.toString()); + if (str == null) { + str = ""; + } + int strLen = str.length(); + if (strLen >= width) { + str.getChars(strLen - width, strLen, buffer, size); + } else { + int padLen = width - strLen; + for (int i = 0; i < padLen; i++) { + buffer[size + i] = padChar; + } + str.getChars(0, strLen, buffer, size + padLen); + } + size += width; + } + return this; + } + + /** + * Appends an object to the builder padding on the left to a fixed width. + * The String.valueOf of the int value is used. + * If the formatted value is larger than the length, the left hand side is lost. + * + * @param value the value to append + * @param width the fixed field width, zero or negative has no effect + * @param padChar the pad character to use + * @return this, to enable chaining + */ + public StrBuilder appendFixedWidthPadLeft(int value, int width, char padChar) { + return appendFixedWidthPadLeft(String.valueOf(value), width, padChar); + } + + /** + * Appends an object to the builder padding on the right to a fixed length. + * The toString of the object is used. + * If the object is larger than the length, the right hand side is lost. + * If the object is null, null text value is used. + * + * @param obj the object to append, null uses null text + * @param width the fixed field width, zero or negative has no effect + * @param padChar the pad character to use + * @return this, to enable chaining + */ + public StrBuilder appendFixedWidthPadRight(Object obj, int width, char padChar) { + if (width > 0) { + ensureCapacity(size + width); + String str = (obj == null ? getNullText() : obj.toString()); + if (str == null) { + str = ""; + } + int strLen = str.length(); + if (strLen >= width) { + str.getChars(0, width, buffer, size); + } else { + int padLen = width - strLen; + str.getChars(0, strLen, buffer, size); + for (int i = 0; i < padLen; i++) { + buffer[size + strLen + i] = padChar; + } + } + size += width; + } + return this; + } + + /** + * Appends an object to the builder padding on the right to a fixed length. + * The String.valueOf of the int value is used. + * If the object is larger than the length, the right hand side is lost. + * + * @param value the value to append + * @param width the fixed field width, zero or negative has no effect + * @param padChar the pad character to use + * @return this, to enable chaining + */ + public StrBuilder appendFixedWidthPadRight(int value, int width, char padChar) { + return appendFixedWidthPadRight(String.valueOf(value), width, padChar); + } + + //----------------------------------------------------------------------- + /** + * Inserts the string representation of an object into this builder. + * Inserting null will use the stored null text value. + * + * @param index the index to add at, must be valid + * @param obj the object to insert + * @return this, to enable chaining + * @throws IndexOutOfBoundsException if the index is invalid + */ + public StrBuilder insert(int index, Object obj) { + if (obj == null) { + return insert(index, nullText); + } + return insert(index, obj.toString()); + } + + /** + * Inserts the string into this builder. + * Inserting null will use the stored null text value. + * + * @param index the index to add at, must be valid + * @param str the string to insert + * @return this, to enable chaining + * @throws IndexOutOfBoundsException if the index is invalid + */ + @SuppressWarnings("null") // str cannot be null + public StrBuilder insert(int index, String str) { + validateIndex(index); + if (str == null) { + str = nullText; + } + int strLen = (str == null ? 0 : str.length()); + if (strLen > 0) { + int newSize = size + strLen; + ensureCapacity(newSize); + System.arraycopy(buffer, index, buffer, index + strLen, size - index); + size = newSize; + str.getChars(0, strLen, buffer, index); // str cannot be null here + } + return this; + } + + /** + * Inserts the character array into this builder. + * Inserting null will use the stored null text value. + * + * @param index the index to add at, must be valid + * @param chars the char array to insert + * @return this, to enable chaining + * @throws IndexOutOfBoundsException if the index is invalid + */ + public StrBuilder insert(int index, char chars[]) { + validateIndex(index); + if (chars == null) { + return insert(index, nullText); + } + int len = chars.length; + if (len > 0) { + ensureCapacity(size + len); + System.arraycopy(buffer, index, buffer, index + len, size - index); + System.arraycopy(chars, 0, buffer, index, len); + size += len; + } + return this; + } + + /** + * Inserts part of the character array into this builder. + * Inserting null will use the stored null text value. + * + * @param index the index to add at, must be valid + * @param chars the char array to insert + * @param offset the offset into the character array to start at, must be valid + * @param length the length of the character array part to copy, must be positive + * @return this, to enable chaining + * @throws IndexOutOfBoundsException if any index is invalid + */ + public StrBuilder insert(int index, char chars[], int offset, int length) { + validateIndex(index); + if (chars == null) { + return insert(index, nullText); + } + if (offset < 0 || offset > chars.length) { + throw new StringIndexOutOfBoundsException("Invalid offset: " + offset); + } + if (length < 0 || offset + length > chars.length) { + throw new StringIndexOutOfBoundsException("Invalid length: " + length); + } + if (length > 0) { + ensureCapacity(size + length); + System.arraycopy(buffer, index, buffer, index + length, size - index); + System.arraycopy(chars, offset, buffer, index, length); + size += length; + } + return this; + } + + /** + * Inserts the value into this builder. + * + * @param index the index to add at, must be valid + * @param value the value to insert + * @return this, to enable chaining + * @throws IndexOutOfBoundsException if the index is invalid + */ + public StrBuilder insert(int index, boolean value) { + validateIndex(index); + if (value) { + ensureCapacity(size + 4); + System.arraycopy(buffer, index, buffer, index + 4, size - index); + buffer[index++] = 't'; + buffer[index++] = 'r'; + buffer[index++] = 'u'; + buffer[index] = 'e'; + size += 4; + } else { + ensureCapacity(size + 5); + System.arraycopy(buffer, index, buffer, index + 5, size - index); + buffer[index++] = 'f'; + buffer[index++] = 'a'; + buffer[index++] = 'l'; + buffer[index++] = 's'; + buffer[index] = 'e'; + size += 5; + } + return this; + } + + /** + * Inserts the value into this builder. + * + * @param index the index to add at, must be valid + * @param value the value to insert + * @return this, to enable chaining + * @throws IndexOutOfBoundsException if the index is invalid + */ + public StrBuilder insert(int index, char value) { + validateIndex(index); + ensureCapacity(size + 1); + System.arraycopy(buffer, index, buffer, index + 1, size - index); + buffer[index] = value; + size++; + return this; + } + + /** + * Inserts the value into this builder. + * + * @param index the index to add at, must be valid + * @param value the value to insert + * @return this, to enable chaining + * @throws IndexOutOfBoundsException if the index is invalid + */ + public StrBuilder insert(int index, int value) { + return insert(index, String.valueOf(value)); + } + + /** + * Inserts the value into this builder. + * + * @param index the index to add at, must be valid + * @param value the value to insert + * @return this, to enable chaining + * @throws IndexOutOfBoundsException if the index is invalid + */ + public StrBuilder insert(int index, long value) { + return insert(index, String.valueOf(value)); + } + + /** + * Inserts the value into this builder. + * + * @param index the index to add at, must be valid + * @param value the value to insert + * @return this, to enable chaining + * @throws IndexOutOfBoundsException if the index is invalid + */ + public StrBuilder insert(int index, float value) { + return insert(index, String.valueOf(value)); + } + + /** + * Inserts the value into this builder. + * + * @param index the index to add at, must be valid + * @param value the value to insert + * @return this, to enable chaining + * @throws IndexOutOfBoundsException if the index is invalid + */ + public StrBuilder insert(int index, double value) { + return insert(index, String.valueOf(value)); + } + + //----------------------------------------------------------------------- + /** + * Internal method to delete a range without validation. + * + * @param startIndex the start index, must be valid + * @param endIndex the end index (exclusive), must be valid + * @param len the length, must be valid + * @throws IndexOutOfBoundsException if any index is invalid + */ + private void deleteImpl(int startIndex, int endIndex, int len) { + System.arraycopy(buffer, endIndex, buffer, startIndex, size - endIndex); + size -= len; + } + + /** + * Deletes the characters between the two specified indices. + * + * @param startIndex the start index, inclusive, must be valid + * @param endIndex the end index, exclusive, must be valid except + * that if too large it is treated as end of string + * @return this, to enable chaining + * @throws IndexOutOfBoundsException if the index is invalid + */ + public StrBuilder delete(int startIndex, int endIndex) { + endIndex = validateRange(startIndex, endIndex); + int len = endIndex - startIndex; + if (len > 0) { + deleteImpl(startIndex, endIndex, len); + } + return this; + } + + //----------------------------------------------------------------------- + /** + * Deletes the character wherever it occurs in the builder. + * + * @param ch the character to delete + * @return this, to enable chaining + */ + public StrBuilder deleteAll(char ch) { + for (int i = 0; i < size; i++) { + if (buffer[i] == ch) { + int start = i; + while (++i < size) { + if (buffer[i] != ch) { + break; + } + } + int len = i - start; + deleteImpl(start, i, len); + i -= len; + } + } + return this; + } + + /** + * Deletes the character wherever it occurs in the builder. + * + * @param ch the character to delete + * @return this, to enable chaining + */ + public StrBuilder deleteFirst(char ch) { + for (int i = 0; i < size; i++) { + if (buffer[i] == ch) { + deleteImpl(i, i + 1, 1); + break; + } + } + return this; + } + + //----------------------------------------------------------------------- + /** + * Deletes the string wherever it occurs in the builder. + * + * @param str the string to delete, null causes no action + * @return this, to enable chaining + */ + public StrBuilder deleteAll(String str) { + int len = (str == null ? 0 : str.length()); + if (len > 0) { + int index = indexOf(str, 0); + while (index >= 0) { + deleteImpl(index, index + len, len); + index = indexOf(str, index); + } + } + return this; + } + + /** + * Deletes the string wherever it occurs in the builder. + * + * @param str the string to delete, null causes no action + * @return this, to enable chaining + */ + public StrBuilder deleteFirst(String str) { + int len = (str == null ? 0 : str.length()); + if (len > 0) { + int index = indexOf(str, 0); + if (index >= 0) { + deleteImpl(index, index + len, len); + } + } + return this; + } + + //----------------------------------------------------------------------- + /** + * Deletes all parts of the builder that the matcher matches. + *

    + * Matchers can be used to perform advanced deletion behaviour. + * For example you could write a matcher to delete all occurances + * where the character 'a' is followed by a number. + * + * @param matcher the matcher to use to find the deletion, null causes no action + * @return this, to enable chaining + */ + public StrBuilder deleteAll(StrMatcher matcher) { + return replace(matcher, null, 0, size, -1); + } + + /** + * Deletes the first match within the builder using the specified matcher. + *

    + * Matchers can be used to perform advanced deletion behaviour. + * For example you could write a matcher to delete + * where the character 'a' is followed by a number. + * + * @param matcher the matcher to use to find the deletion, null causes no action + * @return this, to enable chaining + */ + public StrBuilder deleteFirst(StrMatcher matcher) { + return replace(matcher, null, 0, size, 1); + } + + //----------------------------------------------------------------------- + /** + * Internal method to delete a range without validation. + * + * @param startIndex the start index, must be valid + * @param endIndex the end index (exclusive), must be valid + * @param removeLen the length to remove (endIndex - startIndex), must be valid + * @param insertStr the string to replace with, null means delete range + * @param insertLen the length of the insert string, must be valid + * @throws IndexOutOfBoundsException if any index is invalid + */ + private void replaceImpl(int startIndex, int endIndex, int removeLen, String insertStr, int insertLen) { + int newSize = size - removeLen + insertLen; + if (insertLen != removeLen) { + ensureCapacity(newSize); + System.arraycopy(buffer, endIndex, buffer, startIndex + insertLen, size - endIndex); + size = newSize; + } + if (insertLen > 0) { + insertStr.getChars(0, insertLen, buffer, startIndex); + } + } + + /** + * Replaces a portion of the string builder with another string. + * The length of the inserted string does not have to match the removed length. + * + * @param startIndex the start index, inclusive, must be valid + * @param endIndex the end index, exclusive, must be valid except + * that if too large it is treated as end of string + * @param replaceStr the string to replace with, null means delete range + * @return this, to enable chaining + * @throws IndexOutOfBoundsException if the index is invalid + */ + public StrBuilder replace(int startIndex, int endIndex, String replaceStr) { + endIndex = validateRange(startIndex, endIndex); + int insertLen = (replaceStr == null ? 0 : replaceStr.length()); + replaceImpl(startIndex, endIndex, endIndex - startIndex, replaceStr, insertLen); + return this; + } + + //----------------------------------------------------------------------- + /** + * Replaces the search character with the replace character + * throughout the builder. + * + * @param search the search character + * @param replace the replace character + * @return this, to enable chaining + */ + public StrBuilder replaceAll(char search, char replace) { + if (search != replace) { + for (int i = 0; i < size; i++) { + if (buffer[i] == search) { + buffer[i] = replace; + } + } + } + return this; + } + + /** + * Replaces the first instance of the search character with the + * replace character in the builder. + * + * @param search the search character + * @param replace the replace character + * @return this, to enable chaining + */ + public StrBuilder replaceFirst(char search, char replace) { + if (search != replace) { + for (int i = 0; i < size; i++) { + if (buffer[i] == search) { + buffer[i] = replace; + break; + } + } + } + return this; + } + + //----------------------------------------------------------------------- + /** + * Replaces the search string with the replace string throughout the builder. + * + * @param searchStr the search string, null causes no action to occur + * @param replaceStr the replace string, null is equivalent to an empty string + * @return this, to enable chaining + */ + public StrBuilder replaceAll(String searchStr, String replaceStr) { + int searchLen = (searchStr == null ? 0 : searchStr.length()); + if (searchLen > 0) { + int replaceLen = (replaceStr == null ? 0 : replaceStr.length()); + int index = indexOf(searchStr, 0); + while (index >= 0) { + replaceImpl(index, index + searchLen, searchLen, replaceStr, replaceLen); + index = indexOf(searchStr, index + replaceLen); + } + } + return this; + } + + /** + * Replaces the first instance of the search string with the replace string. + * + * @param searchStr the search string, null causes no action to occur + * @param replaceStr the replace string, null is equivalent to an empty string + * @return this, to enable chaining + */ + public StrBuilder replaceFirst(String searchStr, String replaceStr) { + int searchLen = (searchStr == null ? 0 : searchStr.length()); + if (searchLen > 0) { + int index = indexOf(searchStr, 0); + if (index >= 0) { + int replaceLen = (replaceStr == null ? 0 : replaceStr.length()); + replaceImpl(index, index + searchLen, searchLen, replaceStr, replaceLen); + } + } + return this; + } + + //----------------------------------------------------------------------- + /** + * Replaces all matches within the builder with the replace string. + *

    + * Matchers can be used to perform advanced replace behaviour. + * For example you could write a matcher to replace all occurances + * where the character 'a' is followed by a number. + * + * @param matcher the matcher to use to find the deletion, null causes no action + * @param replaceStr the replace string, null is equivalent to an empty string + * @return this, to enable chaining + */ + public StrBuilder replaceAll(StrMatcher matcher, String replaceStr) { + return replace(matcher, replaceStr, 0, size, -1); + } + + /** + * Replaces the first match within the builder with the replace string. + *

    + * Matchers can be used to perform advanced replace behaviour. + * For example you could write a matcher to replace + * where the character 'a' is followed by a number. + * + * @param matcher the matcher to use to find the deletion, null causes no action + * @param replaceStr the replace string, null is equivalent to an empty string + * @return this, to enable chaining + */ + public StrBuilder replaceFirst(StrMatcher matcher, String replaceStr) { + return replace(matcher, replaceStr, 0, size, 1); + } + + // ----------------------------------------------------------------------- + /** + * Advanced search and replaces within the builder using a matcher. + *

    + * Matchers can be used to perform advanced behaviour. + * For example you could write a matcher to delete all occurances + * where the character 'a' is followed by a number. + * + * @param matcher the matcher to use to find the deletion, null causes no action + * @param replaceStr the string to replace the match with, null is a delete + * @param startIndex the start index, inclusive, must be valid + * @param endIndex the end index, exclusive, must be valid except + * that if too large it is treated as end of string + * @param replaceCount the number of times to replace, -1 for replace all + * @return this, to enable chaining + * @throws IndexOutOfBoundsException if start index is invalid + */ + public StrBuilder replace( + StrMatcher matcher, String replaceStr, + int startIndex, int endIndex, int replaceCount) { + endIndex = validateRange(startIndex, endIndex); + return replaceImpl(matcher, replaceStr, startIndex, endIndex, replaceCount); + } + + /** + * Replaces within the builder using a matcher. + *

    + * Matchers can be used to perform advanced behaviour. + * For example you could write a matcher to delete all occurances + * where the character 'a' is followed by a number. + * + * @param matcher the matcher to use to find the deletion, null causes no action + * @param replaceStr the string to replace the match with, null is a delete + * @param from the start index, must be valid + * @param to the end index (exclusive), must be valid + * @param replaceCount the number of times to replace, -1 for replace all + * @return this, to enable chaining + * @throws IndexOutOfBoundsException if any index is invalid + */ + private StrBuilder replaceImpl( + StrMatcher matcher, String replaceStr, + int from, int to, int replaceCount) { + if (matcher == null || size == 0) { + return this; + } + int replaceLen = (replaceStr == null ? 0 : replaceStr.length()); + char[] buf = buffer; + for (int i = from; i < to && replaceCount != 0; i++) { + int removeLen = matcher.isMatch(buf, i, from, to); + if (removeLen > 0) { + replaceImpl(i, i + removeLen, removeLen, replaceStr, replaceLen); + to = to - removeLen + replaceLen; + i = i + replaceLen - 1; + if (replaceCount > 0) { + replaceCount--; + } + } + } + return this; + } + + //----------------------------------------------------------------------- + /** + * Reverses the string builder placing each character in the opposite index. + * + * @return this, to enable chaining + */ + public StrBuilder reverse() { + if (size == 0) { + return this; + } + + int half = size / 2; + char[] buf = buffer; + for (int leftIdx = 0, rightIdx = size - 1; leftIdx < half; leftIdx++,rightIdx--) { + char swap = buf[leftIdx]; + buf[leftIdx] = buf[rightIdx]; + buf[rightIdx] = swap; + } + return this; + } + + //----------------------------------------------------------------------- + /** + * Trims the builder by removing characters less than or equal to a space + * from the beginning and end. + * + * @return this, to enable chaining + */ + public StrBuilder trim() { + if (size == 0) { + return this; + } + int len = size; + char[] buf = buffer; + int pos = 0; + while (pos < len && buf[pos] <= ' ') { + pos++; + } + while (pos < len && buf[len - 1] <= ' ') { + len--; + } + if (len < size) { + delete(len, size); + } + if (pos > 0) { + delete(0, pos); + } + return this; + } + + //----------------------------------------------------------------------- + /** + * Checks whether this builder starts with the specified string. + *

    + * Note that this method handles null input quietly, unlike String. + * + * @param str the string to search for, null returns false + * @return true if the builder starts with the string + */ + public boolean startsWith(String str) { + if (str == null) { + return false; + } + int len = str.length(); + if (len == 0) { + return true; + } + if (len > size) { + return false; + } + for (int i = 0; i < len; i++) { + if (buffer[i] != str.charAt(i)) { + return false; + } + } + return true; + } + + /** + * Checks whether this builder ends with the specified string. + *

    + * Note that this method handles null input quietly, unlike String. + * + * @param str the string to search for, null returns false + * @return true if the builder ends with the string + */ + public boolean endsWith(String str) { + if (str == null) { + return false; + } + int len = str.length(); + if (len == 0) { + return true; + } + if (len > size) { + return false; + } + int pos = size - len; + for (int i = 0; i < len; i++,pos++) { + if (buffer[pos] != str.charAt(i)) { + return false; + } + } + return true; + } + + //----------------------------------------------------------------------- + /** + * {@inheritDoc} + * @since 3.0 + */ + @Override + public CharSequence subSequence(int startIndex, int endIndex) { + if (startIndex < 0) { + throw new StringIndexOutOfBoundsException(startIndex); + } + if (endIndex > size) { + throw new StringIndexOutOfBoundsException(endIndex); + } + if (startIndex > endIndex) { + throw new StringIndexOutOfBoundsException(endIndex - startIndex); + } + return substring(startIndex, endIndex); + } + + /** + * Extracts a portion of this string builder as a string. + * + * @param start the start index, inclusive, must be valid + * @return the new string + * @throws IndexOutOfBoundsException if the index is invalid + */ + public String substring(int start) { + return substring(start, size); + } + + /** + * Extracts a portion of this string builder as a string. + *

    + * Note: This method treats an endIndex greater than the length of the + * builder as equal to the length of the builder, and continues + * without error, unlike StringBuffer or String. + * + * @param startIndex the start index, inclusive, must be valid + * @param endIndex the end index, exclusive, must be valid except + * that if too large it is treated as end of string + * @return the new string + * @throws IndexOutOfBoundsException if the index is invalid + */ + public String substring(int startIndex, int endIndex) { + endIndex = validateRange(startIndex, endIndex); + return new String(buffer, startIndex, endIndex - startIndex); + } + + /** + * Extracts the leftmost characters from the string builder without + * throwing an exception. + *

    + * This method extracts the left length characters from + * the builder. If this many characters are not available, the whole + * builder is returned. Thus the returned string may be shorter than the + * length requested. + * + * @param length the number of characters to extract, negative returns empty string + * @return the new string + */ + public String leftString(int length) { + if (length <= 0) { + return ""; + } else if (length >= size) { + return new String(buffer, 0, size); + } else { + return new String(buffer, 0, length); + } + } + + /** + * Extracts the rightmost characters from the string builder without + * throwing an exception. + *

    + * This method extracts the right length characters from + * the builder. If this many characters are not available, the whole + * builder is returned. Thus the returned string may be shorter than the + * length requested. + * + * @param length the number of characters to extract, negative returns empty string + * @return the new string + */ + public String rightString(int length) { + if (length <= 0) { + return ""; + } else if (length >= size) { + return new String(buffer, 0, size); + } else { + return new String(buffer, size - length, length); + } + } + + /** + * Extracts some characters from the middle of the string builder without + * throwing an exception. + *

    + * This method extracts length characters from the builder + * at the specified index. + * If the index is negative it is treated as zero. + * If the index is greater than the builder size, it is treated as the builder size. + * If the length is negative, the empty string is returned. + * If insufficient characters are available in the builder, as much as possible is returned. + * Thus the returned string may be shorter than the length requested. + * + * @param index the index to start at, negative means zero + * @param length the number of characters to extract, negative returns empty string + * @return the new string + */ + public String midString(int index, int length) { + if (index < 0) { + index = 0; + } + if (length <= 0 || index >= size) { + return ""; + } + if (size <= index + length) { + return new String(buffer, index, size - index); + } else { + return new String(buffer, index, length); + } + } + + //----------------------------------------------------------------------- + /** + * Checks if the string builder contains the specified char. + * + * @param ch the character to find + * @return true if the builder contains the character + */ + public boolean contains(char ch) { + char[] thisBuf = buffer; + for (int i = 0; i < this.size; i++) { + if (thisBuf[i] == ch) { + return true; + } + } + return false; + } + + /** + * Checks if the string builder contains the specified string. + * + * @param str the string to find + * @return true if the builder contains the string + */ + public boolean contains(String str) { + return indexOf(str, 0) >= 0; + } + + /** + * Checks if the string builder contains a string matched using the + * specified matcher. + *

    + * Matchers can be used to perform advanced searching behaviour. + * For example you could write a matcher to search for the character + * 'a' followed by a number. + * + * @param matcher the matcher to use, null returns -1 + * @return true if the matcher finds a match in the builder + */ + public boolean contains(StrMatcher matcher) { + return indexOf(matcher, 0) >= 0; + } + + //----------------------------------------------------------------------- + /** + * Searches the string builder to find the first reference to the specified char. + * + * @param ch the character to find + * @return the first index of the character, or -1 if not found + */ + public int indexOf(char ch) { + return indexOf(ch, 0); + } + + /** + * Searches the string builder to find the first reference to the specified char. + * + * @param ch the character to find + * @param startIndex the index to start at, invalid index rounded to edge + * @return the first index of the character, or -1 if not found + */ + public int indexOf(char ch, int startIndex) { + startIndex = (startIndex < 0 ? 0 : startIndex); + if (startIndex >= size) { + return -1; + } + char[] thisBuf = buffer; + for (int i = startIndex; i < size; i++) { + if (thisBuf[i] == ch) { + return i; + } + } + return -1; + } + + /** + * Searches the string builder to find the first reference to the specified string. + *

    + * Note that a null input string will return -1, whereas the JDK throws an exception. + * + * @param str the string to find, null returns -1 + * @return the first index of the string, or -1 if not found + */ + public int indexOf(String str) { + return indexOf(str, 0); + } + + /** + * Searches the string builder to find the first reference to the specified + * string starting searching from the given index. + *

    + * Note that a null input string will return -1, whereas the JDK throws an exception. + * + * @param str the string to find, null returns -1 + * @param startIndex the index to start at, invalid index rounded to edge + * @return the first index of the string, or -1 if not found + */ + public int indexOf(String str, int startIndex) { + startIndex = (startIndex < 0 ? 0 : startIndex); + if (str == null || startIndex >= size) { + return -1; + } + int strLen = str.length(); + if (strLen == 1) { + return indexOf(str.charAt(0), startIndex); + } + if (strLen == 0) { + return startIndex; + } + if (strLen > size) { + return -1; + } + char[] thisBuf = buffer; + int len = size - strLen + 1; + outer: + for (int i = startIndex; i < len; i++) { + for (int j = 0; j < strLen; j++) { + if (str.charAt(j) != thisBuf[i + j]) { + continue outer; + } + } + return i; + } + return -1; + } + + /** + * Searches the string builder using the matcher to find the first match. + *

    + * Matchers can be used to perform advanced searching behaviour. + * For example you could write a matcher to find the character 'a' + * followed by a number. + * + * @param matcher the matcher to use, null returns -1 + * @return the first index matched, or -1 if not found + */ + public int indexOf(StrMatcher matcher) { + return indexOf(matcher, 0); + } + + /** + * Searches the string builder using the matcher to find the first + * match searching from the given index. + *

    + * Matchers can be used to perform advanced searching behaviour. + * For example you could write a matcher to find the character 'a' + * followed by a number. + * + * @param matcher the matcher to use, null returns -1 + * @param startIndex the index to start at, invalid index rounded to edge + * @return the first index matched, or -1 if not found + */ + public int indexOf(StrMatcher matcher, int startIndex) { + startIndex = (startIndex < 0 ? 0 : startIndex); + if (matcher == null || startIndex >= size) { + return -1; + } + int len = size; + char[] buf = buffer; + for (int i = startIndex; i < len; i++) { + if (matcher.isMatch(buf, i, startIndex, len) > 0) { + return i; + } + } + return -1; + } + + //----------------------------------------------------------------------- + /** + * Searches the string builder to find the last reference to the specified char. + * + * @param ch the character to find + * @return the last index of the character, or -1 if not found + */ + public int lastIndexOf(char ch) { + return lastIndexOf(ch, size - 1); + } + + /** + * Searches the string builder to find the last reference to the specified char. + * + * @param ch the character to find + * @param startIndex the index to start at, invalid index rounded to edge + * @return the last index of the character, or -1 if not found + */ + public int lastIndexOf(char ch, int startIndex) { + startIndex = (startIndex >= size ? size - 1 : startIndex); + if (startIndex < 0) { + return -1; + } + for (int i = startIndex; i >= 0; i--) { + if (buffer[i] == ch) { + return i; + } + } + return -1; + } + + /** + * Searches the string builder to find the last reference to the specified string. + *

    + * Note that a null input string will return -1, whereas the JDK throws an exception. + * + * @param str the string to find, null returns -1 + * @return the last index of the string, or -1 if not found + */ + public int lastIndexOf(String str) { + return lastIndexOf(str, size - 1); + } + + /** + * Searches the string builder to find the last reference to the specified + * string starting searching from the given index. + *

    + * Note that a null input string will return -1, whereas the JDK throws an exception. + * + * @param str the string to find, null returns -1 + * @param startIndex the index to start at, invalid index rounded to edge + * @return the last index of the string, or -1 if not found + */ + public int lastIndexOf(String str, int startIndex) { + startIndex = (startIndex >= size ? size - 1 : startIndex); + if (str == null || startIndex < 0) { + return -1; + } + int strLen = str.length(); + if (strLen > 0 && strLen <= size) { + if (strLen == 1) { + return lastIndexOf(str.charAt(0), startIndex); + } + + outer: + for (int i = startIndex - strLen + 1; i >= 0; i--) { + for (int j = 0; j < strLen; j++) { + if (str.charAt(j) != buffer[i + j]) { + continue outer; + } + } + return i; + } + + } else if (strLen == 0) { + return startIndex; + } + return -1; + } + + /** + * Searches the string builder using the matcher to find the last match. + *

    + * Matchers can be used to perform advanced searching behaviour. + * For example you could write a matcher to find the character 'a' + * followed by a number. + * + * @param matcher the matcher to use, null returns -1 + * @return the last index matched, or -1 if not found + */ + public int lastIndexOf(StrMatcher matcher) { + return lastIndexOf(matcher, size); + } + + /** + * Searches the string builder using the matcher to find the last + * match searching from the given index. + *

    + * Matchers can be used to perform advanced searching behaviour. + * For example you could write a matcher to find the character 'a' + * followed by a number. + * + * @param matcher the matcher to use, null returns -1 + * @param startIndex the index to start at, invalid index rounded to edge + * @return the last index matched, or -1 if not found + */ + public int lastIndexOf(StrMatcher matcher, int startIndex) { + startIndex = (startIndex >= size ? size - 1 : startIndex); + if (matcher == null || startIndex < 0) { + return -1; + } + char[] buf = buffer; + int endIndex = startIndex + 1; + for (int i = startIndex; i >= 0; i--) { + if (matcher.isMatch(buf, i, 0, endIndex) > 0) { + return i; + } + } + return -1; + } + + //----------------------------------------------------------------------- + /** + * Creates a tokenizer that can tokenize the contents of this builder. + *

    + * This method allows the contents of this builder to be tokenized. + * The tokenizer will be setup by default to tokenize on space, tab, + * newline and formfeed (as per StringTokenizer). These values can be + * changed on the tokenizer class, before retrieving the tokens. + *

    + * The returned tokenizer is linked to this builder. You may intermix + * calls to the buider and tokenizer within certain limits, however + * there is no synchronization. Once the tokenizer has been used once, + * it must be {@link StrTokenizer#reset() reset} to pickup the latest + * changes in the builder. For example: + *

    +     * StrBuilder b = new StrBuilder();
    +     * b.append("a b ");
    +     * StrTokenizer t = b.asTokenizer();
    +     * String[] tokens1 = t.getTokenArray();  // returns a,b
    +     * b.append("c d ");
    +     * String[] tokens2 = t.getTokenArray();  // returns a,b (c and d ignored)
    +     * t.reset();              // reset causes builder changes to be picked up
    +     * String[] tokens3 = t.getTokenArray();  // returns a,b,c,d
    +     * 
    + * In addition to simply intermixing appends and tokenization, you can also + * call the set methods on the tokenizer to alter how it tokenizes. Just + * remember to call reset when you want to pickup builder changes. + *

    + * Calling {@link StrTokenizer#reset(String)} or {@link StrTokenizer#reset(char[])} + * with a non-null value will break the link with the builder. + * + * @return a tokenizer that is linked to this builder + */ + public StrTokenizer asTokenizer() { + return new StrBuilderTokenizer(); + } + + //----------------------------------------------------------------------- + /** + * Gets the contents of this builder as a Reader. + *

    + * This method allows the contents of the builder to be read + * using any standard method that expects a Reader. + *

    + * To use, simply create a StrBuilder, populate it with + * data, call asReader, and then read away. + *

    + * The internal character array is shared between the builder and the reader. + * This allows you to append to the builder after creating the reader, + * and the changes will be picked up. + * Note however, that no synchronization occurs, so you must perform + * all operations with the builder and the reader in one thread. + *

    + * The returned reader supports marking, and ignores the flush method. + * + * @return a reader that reads from this builder + */ + public Reader asReader() { + return new StrBuilderReader(); + } + + //----------------------------------------------------------------------- + /** + * Gets this builder as a Writer that can be written to. + *

    + * This method allows you to populate the contents of the builder + * using any standard method that takes a Writer. + *

    + * To use, simply create a StrBuilder, + * call asWriter, and populate away. The data is available + * at any time using the methods of the StrBuilder. + *

    + * The internal character array is shared between the builder and the writer. + * This allows you to intermix calls that append to the builder and + * write using the writer and the changes will be occur correctly. + * Note however, that no synchronization occurs, so you must perform + * all operations with the builder and the writer in one thread. + *

    + * The returned writer ignores the close and flush methods. + * + * @return a writer that populates this builder + */ + public Writer asWriter() { + return new StrBuilderWriter(); + } + + //----------------------------------------------------------------------- +// /** +// * Gets a String version of the string builder by calling the internal +// * constructor of String by reflection. +// *

    +// * WARNING: You must not use the StrBuilder after calling this method +// * as the buffer is now shared with the String object. To ensure this, +// * the internal character array is set to null, so you will get +// * NullPointerExceptions on all method calls. +// * +// * @return the builder as a String +// */ +// public String toSharedString() { +// try { +// Constructor con = String.class.getDeclaredConstructor( +// new Class[] {int.class, int.class, char[].class}); +// con.setAccessible(true); +// char[] buffer = buf; +// buf = null; +// size = -1; +// nullText = null; +// return (String) con.newInstance( +// new Object[] {Integer.valueOf(0), Integer.valueOf(size), buffer}); +// +// } catch (Exception ex) { +// ex.printStackTrace(); +// throw new UnsupportedOperationException("StrBuilder.toSharedString is unsupported: " + ex.getMessage()); +// } +// } + + //----------------------------------------------------------------------- + /** + * Checks the contents of this builder against another to see if they + * contain the same character content ignoring case. + * + * @param other the object to check, null returns false + * @return true if the builders contain the same characters in the same order + */ + public boolean equalsIgnoreCase(StrBuilder other) { + if (this == other) { + return true; + } + if (this.size != other.size) { + return false; + } + char thisBuf[] = this.buffer; + char otherBuf[] = other.buffer; + for (int i = size - 1; i >= 0; i--) { + char c1 = thisBuf[i]; + char c2 = otherBuf[i]; + if (c1 != c2 && Character.toUpperCase(c1) != Character.toUpperCase(c2)) { + return false; + } + } + return true; + } + + /** + * Checks the contents of this builder against another to see if they + * contain the same character content. + * + * @param other the object to check, null returns false + * @return true if the builders contain the same characters in the same order + */ + public boolean equals(StrBuilder other) { + if (this == other) { + return true; + } + if (this.size != other.size) { + return false; + } + char thisBuf[] = this.buffer; + char otherBuf[] = other.buffer; + for (int i = size - 1; i >= 0; i--) { + if (thisBuf[i] != otherBuf[i]) { + return false; + } + } + return true; + } + + /** + * Checks the contents of this builder against another to see if they + * contain the same character content. + * + * @param obj the object to check, null returns false + * @return true if the builders contain the same characters in the same order + */ + @Override + public boolean equals(Object obj) { + if (obj instanceof StrBuilder) { + return equals((StrBuilder) obj); + } + return false; + } + + /** + * Gets a suitable hash code for this builder. + * + * @return a hash code + */ + @Override + public int hashCode() { + char buf[] = buffer; + int hash = 0; + for (int i = size - 1; i >= 0; i--) { + hash = 31 * hash + buf[i]; + } + return hash; + } + + //----------------------------------------------------------------------- + /** + * Gets a String version of the string builder, creating a new instance + * each time the method is called. + *

    + * Note that unlike StringBuffer, the string version returned is + * independent of the string builder. + * + * @return the builder as a String + */ + @Override + public String toString() { + return new String(buffer, 0, size); + } + + /** + * Gets a StringBuffer version of the string builder, creating a + * new instance each time the method is called. + * + * @return the builder as a StringBuffer + */ + public StringBuffer toStringBuffer() { + return new StringBuffer(size).append(buffer, 0, size); + } + + //----------------------------------------------------------------------- + /** + * Validates parameters defining a range of the builder. + * + * @param startIndex the start index, inclusive, must be valid + * @param endIndex the end index, exclusive, must be valid except + * that if too large it is treated as end of string + * @return the new string + * @throws IndexOutOfBoundsException if the index is invalid + */ + protected int validateRange(int startIndex, int endIndex) { + if (startIndex < 0) { + throw new StringIndexOutOfBoundsException(startIndex); + } + if (endIndex > size) { + endIndex = size; + } + if (startIndex > endIndex) { + throw new StringIndexOutOfBoundsException("end < start"); + } + return endIndex; + } + + /** + * Validates parameters defining a single index in the builder. + * + * @param index the index, must be valid + * @throws IndexOutOfBoundsException if the index is invalid + */ + protected void validateIndex(int index) { + if (index < 0 || index > size) { + throw new StringIndexOutOfBoundsException(index); + } + } + + //----------------------------------------------------------------------- + /** + * Inner class to allow StrBuilder to operate as a tokenizer. + */ + class StrBuilderTokenizer extends StrTokenizer { + + /** + * Default constructor. + */ + StrBuilderTokenizer() { + super(); + } + + /** {@inheritDoc} */ + @Override + protected List tokenize(char[] chars, int offset, int count) { + if (chars == null) { + return super.tokenize(StrBuilder.this.buffer, 0, StrBuilder.this.size()); + } else { + return super.tokenize(chars, offset, count); + } + } + + /** {@inheritDoc} */ + @Override + public String getContent() { + String str = super.getContent(); + if (str == null) { + return StrBuilder.this.toString(); + } else { + return str; + } + } + } + + //----------------------------------------------------------------------- + /** + * Inner class to allow StrBuilder to operate as a writer. + */ + class StrBuilderReader extends Reader { + /** The current stream position. */ + private int pos; + /** The last mark position. */ + private int mark; + + /** + * Default constructor. + */ + StrBuilderReader() { + super(); + } + + /** {@inheritDoc} */ + @Override + public void close() { + // do nothing + } + + /** {@inheritDoc} */ + @Override + public int read() { + if (ready() == false) { + return -1; + } + return StrBuilder.this.charAt(pos++); + } + + /** {@inheritDoc} */ + @Override + public int read(char b[], int off, int len) { + if (off < 0 || len < 0 || off > b.length || + (off + len) > b.length || (off + len) < 0) { + throw new IndexOutOfBoundsException(); + } + if (len == 0) { + return 0; + } + if (pos >= StrBuilder.this.size()) { + return -1; + } + if (pos + len > size()) { + len = StrBuilder.this.size() - pos; + } + StrBuilder.this.getChars(pos, pos + len, b, off); + pos += len; + return len; + } + + /** {@inheritDoc} */ + @Override + public long skip(long n) { + if (pos + n > StrBuilder.this.size()) { + n = StrBuilder.this.size() - pos; + } + if (n < 0) { + return 0; + } + pos += n; + return n; + } + + /** {@inheritDoc} */ + @Override + public boolean ready() { + return pos < StrBuilder.this.size(); + } + + /** {@inheritDoc} */ + @Override + public boolean markSupported() { + return true; + } + + /** {@inheritDoc} */ + @Override + public void mark(int readAheadLimit) { + mark = pos; + } + + /** {@inheritDoc} */ + @Override + public void reset() { + pos = mark; + } + } + + //----------------------------------------------------------------------- + /** + * Inner class to allow StrBuilder to operate as a writer. + */ + class StrBuilderWriter extends Writer { + + /** + * Default constructor. + */ + StrBuilderWriter() { + super(); + } + + /** {@inheritDoc} */ + @Override + public void close() { + // do nothing + } + + /** {@inheritDoc} */ + @Override + public void flush() { + // do nothing + } + + /** {@inheritDoc} */ + @Override + public void write(int c) { + StrBuilder.this.append((char) c); + } + + /** {@inheritDoc} */ + @Override + public void write(char[] cbuf) { + StrBuilder.this.append(cbuf); + } + + /** {@inheritDoc} */ + @Override + public void write(char[] cbuf, int off, int len) { + StrBuilder.this.append(cbuf, off, len); + } + + /** {@inheritDoc} */ + @Override + public void write(String str) { + StrBuilder.this.append(str); + } + + /** {@inheritDoc} */ + @Override + public void write(String str, int off, int len) { + StrBuilder.this.append(str, off, len); + } + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/text/StrBuilderAppendInsertTest.java b/ApacheCommonsLang/org/apache/commons/lang3/text/StrBuilderAppendInsertTest.java new file mode 100644 index 0000000..b39b1d7 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/text/StrBuilderAppendInsertTest.java @@ -0,0 +1,1370 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.lang3.text; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; + +import junit.framework.TestCase; + +import org.apache.commons.lang3.SystemUtils; + +/** + * Unit tests for {@link org.apache.commons.lang3.text.StrBuilder}. + * + * @version $Id: StrBuilderAppendInsertTest.java 1153484 2011-08-03 13:39:42Z ggregory $ + */ +public class StrBuilderAppendInsertTest extends TestCase { + + /** The system line separator. */ + private static final String SEP = SystemUtils.LINE_SEPARATOR; + + /** Test subclass of Object, with a toString method. */ + private static final Object FOO = new Object() { + @Override + public String toString() { + return "foo"; + } + }; + + /** + * Create a new test case with the specified name. + * + * @param name the name + */ + public StrBuilderAppendInsertTest(String name) { + super(name); + } + + //----------------------------------------------------------------------- + public void testAppendNewLine() { + StrBuilder sb = new StrBuilder("---"); + sb.appendNewLine().append("+++"); + assertEquals("---" + SEP + "+++", sb.toString()); + + sb = new StrBuilder("---"); + sb.setNewLineText("#").appendNewLine().setNewLineText(null).appendNewLine(); + assertEquals("---#" + SEP, sb.toString()); + } + + //----------------------------------------------------------------------- + public void testAppendWithNullText() { + StrBuilder sb = new StrBuilder(); + sb.setNullText("NULL"); + assertEquals("", sb.toString()); + + sb.appendNull(); + assertEquals("NULL", sb.toString()); + + sb.append((Object) null); + assertEquals("NULLNULL", sb.toString()); + + sb.append(FOO); + assertEquals("NULLNULLfoo", sb.toString()); + + sb.append((String) null); + assertEquals("NULLNULLfooNULL", sb.toString()); + + sb.append(""); + assertEquals("NULLNULLfooNULL", sb.toString()); + + sb.append("bar"); + assertEquals("NULLNULLfooNULLbar", sb.toString()); + + sb.append((StringBuffer) null); + assertEquals("NULLNULLfooNULLbarNULL", sb.toString()); + + sb.append(new StringBuffer("baz")); + assertEquals("NULLNULLfooNULLbarNULLbaz", sb.toString()); + } + + //----------------------------------------------------------------------- + public void testAppend_Object() { + StrBuilder sb = new StrBuilder(); + sb.appendNull(); + assertEquals("", sb.toString()); + + sb.append((Object) null); + assertEquals("", sb.toString()); + + sb.append(FOO); + assertEquals("foo", sb.toString()); + + sb.append((StringBuffer) null); + assertEquals("foo", sb.toString()); + + sb.append(new StringBuffer("baz")); + assertEquals("foobaz", sb.toString()); + + sb.append(new StrBuilder("yes")); + assertEquals("foobazyes", sb.toString()); + + sb.append((CharSequence) "Seq"); + assertEquals("foobazyesSeq", sb.toString()); + } + + //----------------------------------------------------------------------- + public void testAppend_String() { + StrBuilder sb = new StrBuilder(); + sb.setNullText("NULL").append((String) null); + assertEquals("NULL", sb.toString()); + + sb = new StrBuilder(); + sb.append("foo"); + assertEquals("foo", sb.toString()); + + sb.append(""); + assertEquals("foo", sb.toString()); + + sb.append("bar"); + assertEquals("foobar", sb.toString()); + } + + //----------------------------------------------------------------------- + public void testAppend_String_int_int() { + StrBuilder sb = new StrBuilder(); + sb.setNullText("NULL").append((String) null, 0, 1); + assertEquals("NULL", sb.toString()); + + sb = new StrBuilder(); + sb.append("foo", 0, 3); + assertEquals("foo", sb.toString()); + + try { + sb.append("bar", -1, 1); + fail("append(char[], -1,) expected IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + sb.append("bar", 3, 1); + fail("append(char[], 3,) expected IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + sb.append("bar", 1, -1); + fail("append(char[],, -1) expected IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + sb.append("bar", 1, 3); + fail("append(char[], 1, 3) expected IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + sb.append("bar", -1, 3); + fail("append(char[], -1, 3) expected IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + sb.append("bar", 4, 0); + fail("append(char[], 4, 0) expected IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + + sb.append("bar", 3, 0); + assertEquals("foo", sb.toString()); + + sb.append("abcbardef", 3, 3); + assertEquals("foobar", sb.toString()); + + sb.append( (CharSequence)"abcbardef", 4, 3); + assertEquals("foobarard", sb.toString()); + } + + //----------------------------------------------------------------------- + public void testAppend_StringBuffer() { + StrBuilder sb = new StrBuilder(); + sb.setNullText("NULL").append((StringBuffer) null); + assertEquals("NULL", sb.toString()); + + sb = new StrBuilder(); + sb.append(new StringBuffer("foo")); + assertEquals("foo", sb.toString()); + + sb.append(new StringBuffer("")); + assertEquals("foo", sb.toString()); + + sb.append(new StringBuffer("bar")); + assertEquals("foobar", sb.toString()); + } + + //----------------------------------------------------------------------- + public void testAppend_StringBuffer_int_int() { + StrBuilder sb = new StrBuilder(); + sb.setNullText("NULL").append((StringBuffer) null, 0, 1); + assertEquals("NULL", sb.toString()); + + sb = new StrBuilder(); + sb.append(new StringBuffer("foo"), 0, 3); + assertEquals("foo", sb.toString()); + + try { + sb.append(new StringBuffer("bar"), -1, 1); + fail("append(char[], -1,) expected IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + sb.append(new StringBuffer("bar"), 3, 1); + fail("append(char[], 3,) expected IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + sb.append(new StringBuffer("bar"), 1, -1); + fail("append(char[],, -1) expected IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + sb.append(new StringBuffer("bar"), 1, 3); + fail("append(char[], 1, 3) expected IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + sb.append(new StringBuffer("bar"), -1, 3); + fail("append(char[], -1, 3) expected IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + sb.append(new StringBuffer("bar"), 4, 0); + fail("append(char[], 4, 0) expected IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + + sb.append(new StringBuffer("bar"), 3, 0); + assertEquals("foo", sb.toString()); + + sb.append(new StringBuffer("abcbardef"), 3, 3); + assertEquals("foobar", sb.toString()); + } + + //----------------------------------------------------------------------- + public void testAppend_StrBuilder() { + StrBuilder sb = new StrBuilder(); + sb.setNullText("NULL").append((StrBuilder) null); + assertEquals("NULL", sb.toString()); + + sb = new StrBuilder(); + sb.append(new StrBuilder("foo")); + assertEquals("foo", sb.toString()); + + sb.append(new StrBuilder("")); + assertEquals("foo", sb.toString()); + + sb.append(new StrBuilder("bar")); + assertEquals("foobar", sb.toString()); + } + + //----------------------------------------------------------------------- + public void testAppend_StrBuilder_int_int() { + StrBuilder sb = new StrBuilder(); + sb.setNullText("NULL").append((StrBuilder) null, 0, 1); + assertEquals("NULL", sb.toString()); + + sb = new StrBuilder(); + sb.append(new StrBuilder("foo"), 0, 3); + assertEquals("foo", sb.toString()); + + try { + sb.append(new StrBuilder("bar"), -1, 1); + fail("append(char[], -1,) expected IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + sb.append(new StrBuilder("bar"), 3, 1); + fail("append(char[], 3,) expected IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + sb.append(new StrBuilder("bar"), 1, -1); + fail("append(char[],, -1) expected IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + sb.append(new StrBuilder("bar"), 1, 3); + fail("append(char[], 1, 3) expected IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + sb.append(new StrBuilder("bar"), -1, 3); + fail("append(char[], -1, 3) expected IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + sb.append(new StrBuilder("bar"), 4, 0); + fail("append(char[], 4, 0) expected IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + + sb.append(new StrBuilder("bar"), 3, 0); + assertEquals("foo", sb.toString()); + + sb.append(new StrBuilder("abcbardef"), 3, 3); + assertEquals("foobar", sb.toString()); + } + + //----------------------------------------------------------------------- + public void testAppend_CharArray() { + StrBuilder sb = new StrBuilder(); + sb.setNullText("NULL").append((char[]) null); + assertEquals("NULL", sb.toString()); + + sb = new StrBuilder(); + sb.append(new char[0]); + assertEquals("", sb.toString()); + + sb.append(new char[]{'f', 'o', 'o'}); + assertEquals("foo", sb.toString()); + } + + //----------------------------------------------------------------------- + public void testAppend_CharArray_int_int() { + StrBuilder sb = new StrBuilder(); + sb.setNullText("NULL").append((char[]) null, 0, 1); + assertEquals("NULL", sb.toString()); + + sb = new StrBuilder(); + sb.append(new char[]{'f', 'o', 'o'}, 0, 3); + assertEquals("foo", sb.toString()); + + try { + sb.append(new char[]{'b', 'a', 'r'}, -1, 1); + fail("append(char[], -1,) expected IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + sb.append(new char[]{'b', 'a', 'r'}, 3, 1); + fail("append(char[], 3,) expected IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + sb.append(new char[]{'b', 'a', 'r'}, 1, -1); + fail("append(char[],, -1) expected IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + sb.append(new char[]{'b', 'a', 'r'}, 1, 3); + fail("append(char[], 1, 3) expected IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + sb.append(new char[]{'b', 'a', 'r'}, -1, 3); + fail("append(char[], -1, 3) expected IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + sb.append(new char[]{'b', 'a', 'r'}, 4, 0); + fail("append(char[], 4, 0) expected IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + + sb.append(new char[]{'b', 'a', 'r'}, 3, 0); + assertEquals("foo", sb.toString()); + + sb.append(new char[]{'a', 'b', 'c', 'b', 'a', 'r', 'd', 'e', 'f'}, 3, 3); + assertEquals("foobar", sb.toString()); + } + + //----------------------------------------------------------------------- + public void testAppend_Boolean() { + StrBuilder sb = new StrBuilder(); + sb.append(true); + assertEquals("true", sb.toString()); + + sb.append(false); + assertEquals("truefalse", sb.toString()); + + sb.append('!'); + assertEquals("truefalse!", sb.toString()); + } + + //----------------------------------------------------------------------- + public void testAppend_PrimitiveNumber() { + StrBuilder sb = new StrBuilder(); + sb.append(0); + assertEquals("0", sb.toString()); + + sb.append(1L); + assertEquals("01", sb.toString()); + + sb.append(2.3f); + assertEquals("012.3", sb.toString()); + + sb.append(4.5d); + assertEquals("012.34.5", sb.toString()); + } + + //----------------------------------------------------------------------- + public void testAppendln_Object() { + StrBuilder sb = new StrBuilder(); + sb.appendln((Object) null); + assertEquals("" + SEP, sb.toString()); + + sb.appendln(FOO); + assertEquals(SEP + "foo" + SEP, sb.toString()); + + sb.appendln(Integer.valueOf(6)); + assertEquals(SEP + "foo" + SEP + "6" + SEP, sb.toString()); + } + + //----------------------------------------------------------------------- + public void testAppendln_String() { + final int[] count = new int[2]; + StrBuilder sb = new StrBuilder() { + @Override + public StrBuilder append(String str) { + count[0]++; + return super.append(str); + } + @Override + public StrBuilder appendNewLine() { + count[1]++; + return super.appendNewLine(); + } + }; + sb.appendln("foo"); + assertEquals("foo" + SEP, sb.toString()); + assertEquals(2, count[0]); // appendNewLine() calls append(String) + assertEquals(1, count[1]); + } + + //----------------------------------------------------------------------- + public void testAppendln_String_int_int() { + final int[] count = new int[2]; + StrBuilder sb = new StrBuilder() { + @Override + public StrBuilder append(String str, int startIndex, int length) { + count[0]++; + return super.append(str, startIndex, length); + } + @Override + public StrBuilder appendNewLine() { + count[1]++; + return super.appendNewLine(); + } + }; + sb.appendln("foo", 0, 3); + assertEquals("foo" + SEP, sb.toString()); + assertEquals(1, count[0]); + assertEquals(1, count[1]); + } + + //----------------------------------------------------------------------- + public void testAppendln_StringBuffer() { + final int[] count = new int[2]; + StrBuilder sb = new StrBuilder() { + @Override + public StrBuilder append(StringBuffer str) { + count[0]++; + return super.append(str); + } + @Override + public StrBuilder appendNewLine() { + count[1]++; + return super.appendNewLine(); + } + }; + sb.appendln(new StringBuffer("foo")); + assertEquals("foo" + SEP, sb.toString()); + assertEquals(1, count[0]); + assertEquals(1, count[1]); + } + + //----------------------------------------------------------------------- + public void testAppendln_StringBuffer_int_int() { + final int[] count = new int[2]; + StrBuilder sb = new StrBuilder() { + @Override + public StrBuilder append(StringBuffer str, int startIndex, int length) { + count[0]++; + return super.append(str, startIndex, length); + } + @Override + public StrBuilder appendNewLine() { + count[1]++; + return super.appendNewLine(); + } + }; + sb.appendln(new StringBuffer("foo"), 0, 3); + assertEquals("foo" + SEP, sb.toString()); + assertEquals(1, count[0]); + assertEquals(1, count[1]); + } + + //----------------------------------------------------------------------- + public void testAppendln_StrBuilder() { + final int[] count = new int[2]; + StrBuilder sb = new StrBuilder() { + @Override + public StrBuilder append(StrBuilder str) { + count[0]++; + return super.append(str); + } + @Override + public StrBuilder appendNewLine() { + count[1]++; + return super.appendNewLine(); + } + }; + sb.appendln(new StrBuilder("foo")); + assertEquals("foo" + SEP, sb.toString()); + assertEquals(1, count[0]); + assertEquals(1, count[1]); + } + + //----------------------------------------------------------------------- + public void testAppendln_StrBuilder_int_int() { + final int[] count = new int[2]; + StrBuilder sb = new StrBuilder() { + @Override + public StrBuilder append(StrBuilder str, int startIndex, int length) { + count[0]++; + return super.append(str, startIndex, length); + } + @Override + public StrBuilder appendNewLine() { + count[1]++; + return super.appendNewLine(); + } + }; + sb.appendln(new StrBuilder("foo"), 0, 3); + assertEquals("foo" + SEP, sb.toString()); + assertEquals(1, count[0]); + assertEquals(1, count[1]); + } + + //----------------------------------------------------------------------- + public void testAppendln_CharArray() { + final int[] count = new int[2]; + StrBuilder sb = new StrBuilder() { + @Override + public StrBuilder append(char[] str) { + count[0]++; + return super.append(str); + } + @Override + public StrBuilder appendNewLine() { + count[1]++; + return super.appendNewLine(); + } + }; + sb.appendln("foo".toCharArray()); + assertEquals("foo" + SEP, sb.toString()); + assertEquals(1, count[0]); + assertEquals(1, count[1]); + } + + //----------------------------------------------------------------------- + public void testAppendln_CharArray_int_int() { + final int[] count = new int[2]; + StrBuilder sb = new StrBuilder() { + @Override + public StrBuilder append(char[] str, int startIndex, int length) { + count[0]++; + return super.append(str, startIndex, length); + } + @Override + public StrBuilder appendNewLine() { + count[1]++; + return super.appendNewLine(); + } + }; + sb.appendln("foo".toCharArray(), 0, 3); + assertEquals("foo" + SEP, sb.toString()); + assertEquals(1, count[0]); + assertEquals(1, count[1]); + } + + //----------------------------------------------------------------------- + public void testAppendln_Boolean() { + StrBuilder sb = new StrBuilder(); + sb.appendln(true); + assertEquals("true" + SEP, sb.toString()); + + sb.clear(); + sb.appendln(false); + assertEquals("false" + SEP, sb.toString()); + } + + //----------------------------------------------------------------------- + public void testAppendln_PrimitiveNumber() { + StrBuilder sb = new StrBuilder(); + sb.appendln(0); + assertEquals("0" + SEP, sb.toString()); + + sb.clear(); + sb.appendln(1L); + assertEquals("1" + SEP, sb.toString()); + + sb.clear(); + sb.appendln(2.3f); + assertEquals("2.3" + SEP, sb.toString()); + + sb.clear(); + sb.appendln(4.5d); + assertEquals("4.5" + SEP, sb.toString()); + } + + //----------------------------------------------------------------------- + public void testAppendPadding() { + StrBuilder sb = new StrBuilder(); + sb.append("foo"); + assertEquals("foo", sb.toString()); + + sb.appendPadding(-1, '-'); + assertEquals("foo", sb.toString()); + + sb.appendPadding(0, '-'); + assertEquals("foo", sb.toString()); + + sb.appendPadding(1, '-'); + assertEquals("foo-", sb.toString()); + + sb.appendPadding(16, '-'); + assertEquals(20, sb.length()); + // 12345678901234567890 + assertEquals("foo-----------------", sb.toString()); + } + + //----------------------------------------------------------------------- + public void testAppendFixedWidthPadLeft() { + StrBuilder sb = new StrBuilder(); + sb.appendFixedWidthPadLeft("foo", -1, '-'); + assertEquals("", sb.toString()); + + sb.clear(); + sb.appendFixedWidthPadLeft("foo", 0, '-'); + assertEquals("", sb.toString()); + + sb.clear(); + sb.appendFixedWidthPadLeft("foo", 1, '-'); + assertEquals("o", sb.toString()); + + sb.clear(); + sb.appendFixedWidthPadLeft("foo", 2, '-'); + assertEquals("oo", sb.toString()); + + sb.clear(); + sb.appendFixedWidthPadLeft("foo", 3, '-'); + assertEquals("foo", sb.toString()); + + sb.clear(); + sb.appendFixedWidthPadLeft("foo", 4, '-'); + assertEquals("-foo", sb.toString()); + + sb.clear(); + sb.appendFixedWidthPadLeft("foo", 10, '-'); + assertEquals(10, sb.length()); + // 1234567890 + assertEquals("-------foo", sb.toString()); + + sb.clear(); + sb.setNullText("null"); + sb.appendFixedWidthPadLeft(null, 5, '-'); + assertEquals("-null", sb.toString()); + } + + //----------------------------------------------------------------------- + public void testAppendFixedWidthPadLeft_int() { + StrBuilder sb = new StrBuilder(); + sb.appendFixedWidthPadLeft(123, -1, '-'); + assertEquals("", sb.toString()); + + sb.clear(); + sb.appendFixedWidthPadLeft(123, 0, '-'); + assertEquals("", sb.toString()); + + sb.clear(); + sb.appendFixedWidthPadLeft(123, 1, '-'); + assertEquals("3", sb.toString()); + + sb.clear(); + sb.appendFixedWidthPadLeft(123, 2, '-'); + assertEquals("23", sb.toString()); + + sb.clear(); + sb.appendFixedWidthPadLeft(123, 3, '-'); + assertEquals("123", sb.toString()); + + sb.clear(); + sb.appendFixedWidthPadLeft(123, 4, '-'); + assertEquals("-123", sb.toString()); + + sb.clear(); + sb.appendFixedWidthPadLeft(123, 10, '-'); + assertEquals(10, sb.length()); + // 1234567890 + assertEquals("-------123", sb.toString()); + } + + //----------------------------------------------------------------------- + public void testAppendFixedWidthPadRight() { + StrBuilder sb = new StrBuilder(); + sb.appendFixedWidthPadRight("foo", -1, '-'); + assertEquals("", sb.toString()); + + sb.clear(); + sb.appendFixedWidthPadRight("foo", 0, '-'); + assertEquals("", sb.toString()); + + sb.clear(); + sb.appendFixedWidthPadRight("foo", 1, '-'); + assertEquals("f", sb.toString()); + + sb.clear(); + sb.appendFixedWidthPadRight("foo", 2, '-'); + assertEquals("fo", sb.toString()); + + sb.clear(); + sb.appendFixedWidthPadRight("foo", 3, '-'); + assertEquals("foo", sb.toString()); + + sb.clear(); + sb.appendFixedWidthPadRight("foo", 4, '-'); + assertEquals("foo-", sb.toString()); + + sb.clear(); + sb.appendFixedWidthPadRight("foo", 10, '-'); + assertEquals(10, sb.length()); + // 1234567890 + assertEquals("foo-------", sb.toString()); + + sb.clear(); + sb.setNullText("null"); + sb.appendFixedWidthPadRight(null, 5, '-'); + assertEquals("null-", sb.toString()); + } + + // See: http://issues.apache.org/jira/browse/LANG-299 + public void testLang299() { + StrBuilder sb = new StrBuilder(1); + sb.appendFixedWidthPadRight("foo", 1, '-'); + assertEquals("f", sb.toString()); + } + + //----------------------------------------------------------------------- + public void testAppendFixedWidthPadRight_int() { + StrBuilder sb = new StrBuilder(); + sb.appendFixedWidthPadRight(123, -1, '-'); + assertEquals("", sb.toString()); + + sb.clear(); + sb.appendFixedWidthPadRight(123, 0, '-'); + assertEquals("", sb.toString()); + + sb.clear(); + sb.appendFixedWidthPadRight(123, 1, '-'); + assertEquals("1", sb.toString()); + + sb.clear(); + sb.appendFixedWidthPadRight(123, 2, '-'); + assertEquals("12", sb.toString()); + + sb.clear(); + sb.appendFixedWidthPadRight(123, 3, '-'); + assertEquals("123", sb.toString()); + + sb.clear(); + sb.appendFixedWidthPadRight(123, 4, '-'); + assertEquals("123-", sb.toString()); + + sb.clear(); + sb.appendFixedWidthPadRight(123, 10, '-'); + assertEquals(10, sb.length()); + // 1234567890 + assertEquals("123-------", sb.toString()); + } + + //----------------------------------------------------------------------- + public void testAppendAll_Array() { + StrBuilder sb = new StrBuilder(); + sb.appendAll((Object[]) null); + assertEquals("", sb.toString()); + + sb.clear(); + sb.appendAll(new Object[0]); + assertEquals("", sb.toString()); + + sb.clear(); + sb.appendAll(new Object[]{"foo", "bar", "baz"}); + assertEquals("foobarbaz", sb.toString()); + } + + //----------------------------------------------------------------------- + public void testAppendAll_Collection() { + StrBuilder sb = new StrBuilder(); + sb.appendAll((Collection) null); + assertEquals("", sb.toString()); + + sb.clear(); + sb.appendAll(Collections.EMPTY_LIST); + assertEquals("", sb.toString()); + + sb.clear(); + sb.appendAll(Arrays.asList(new Object[]{"foo", "bar", "baz"})); + assertEquals("foobarbaz", sb.toString()); + } + + //----------------------------------------------------------------------- + public void testAppendAll_Iterator() { + StrBuilder sb = new StrBuilder(); + sb.appendAll((Iterator) null); + assertEquals("", sb.toString()); + + sb.clear(); + sb.appendAll(Collections.EMPTY_LIST.iterator()); + assertEquals("", sb.toString()); + + sb.clear(); + sb.appendAll(Arrays.asList(new Object[]{"foo", "bar", "baz"}).iterator()); + assertEquals("foobarbaz", sb.toString()); + } + + //----------------------------------------------------------------------- + public void testAppendWithSeparators_Array() { + StrBuilder sb = new StrBuilder(); + sb.appendWithSeparators((Object[]) null, ","); + assertEquals("", sb.toString()); + + sb.clear(); + sb.appendWithSeparators(new Object[0], ","); + assertEquals("", sb.toString()); + + sb.clear(); + sb.appendWithSeparators(new Object[]{"foo", "bar", "baz"}, ","); + assertEquals("foo,bar,baz", sb.toString()); + + sb.clear(); + sb.appendWithSeparators(new Object[]{"foo", "bar", "baz"}, null); + assertEquals("foobarbaz", sb.toString()); + + sb.clear(); + sb.appendWithSeparators(new Object[]{"foo", null, "baz"}, ","); + assertEquals("foo,,baz", sb.toString()); + } + + //----------------------------------------------------------------------- + public void testAppendWithSeparators_Collection() { + StrBuilder sb = new StrBuilder(); + sb.appendWithSeparators((Collection) null, ","); + assertEquals("", sb.toString()); + + sb.clear(); + sb.appendWithSeparators(Collections.EMPTY_LIST, ","); + assertEquals("", sb.toString()); + + sb.clear(); + sb.appendWithSeparators(Arrays.asList(new Object[]{"foo", "bar", "baz"}), ","); + assertEquals("foo,bar,baz", sb.toString()); + + sb.clear(); + sb.appendWithSeparators(Arrays.asList(new Object[]{"foo", "bar", "baz"}), null); + assertEquals("foobarbaz", sb.toString()); + + sb.clear(); + sb.appendWithSeparators(Arrays.asList(new Object[]{"foo", null, "baz"}), ","); + assertEquals("foo,,baz", sb.toString()); + } + + //----------------------------------------------------------------------- + public void testAppendWithSeparators_Iterator() { + StrBuilder sb = new StrBuilder(); + sb.appendWithSeparators((Iterator) null, ","); + assertEquals("", sb.toString()); + + sb.clear(); + sb.appendWithSeparators(Collections.EMPTY_LIST.iterator(), ","); + assertEquals("", sb.toString()); + + sb.clear(); + sb.appendWithSeparators(Arrays.asList(new Object[]{"foo", "bar", "baz"}).iterator(), ","); + assertEquals("foo,bar,baz", sb.toString()); + + sb.clear(); + sb.appendWithSeparators(Arrays.asList(new Object[]{"foo", "bar", "baz"}).iterator(), null); + assertEquals("foobarbaz", sb.toString()); + + sb.clear(); + sb.appendWithSeparators(Arrays.asList(new Object[]{"foo", null, "baz"}).iterator(), ","); + assertEquals("foo,,baz", sb.toString()); + } + + //----------------------------------------------------------------------- + public void testAppendWithSeparatorsWithNullText() { + StrBuilder sb = new StrBuilder(); + sb.setNullText("null"); + sb.appendWithSeparators(new Object[]{"foo", null, "baz"}, ","); + assertEquals("foo,null,baz", sb.toString()); + + sb.clear(); + sb.appendWithSeparators(Arrays.asList(new Object[]{"foo", null, "baz"}), ","); + assertEquals("foo,null,baz", sb.toString()); + } + + //----------------------------------------------------------------------- + public void testAppendSeparator_String() { + StrBuilder sb = new StrBuilder(); + sb.appendSeparator(","); // no effect + assertEquals("", sb.toString()); + sb.append("foo"); + assertEquals("foo", sb.toString()); + sb.appendSeparator(","); + assertEquals("foo,", sb.toString()); + } + + //----------------------------------------------------------------------- + public void testAppendSeparator_String_String() { + StrBuilder sb = new StrBuilder(); + final String startSeparator = "order by "; + final String standardSeparator = ","; + final String foo = "foo"; + sb.appendSeparator(null, null); + assertEquals("", sb.toString()); + sb.appendSeparator(standardSeparator, null); + assertEquals("", sb.toString()); + sb.appendSeparator(standardSeparator, startSeparator); + assertEquals(startSeparator, sb.toString()); + sb.appendSeparator(null, null); + assertEquals(startSeparator, sb.toString()); + sb.appendSeparator(null, startSeparator); + assertEquals(startSeparator, sb.toString()); + sb.append(foo); + assertEquals(startSeparator + foo, sb.toString()); + sb.appendSeparator(standardSeparator, startSeparator); + assertEquals(startSeparator + foo + standardSeparator, sb.toString()); + } + + //----------------------------------------------------------------------- + public void testAppendSeparator_char() { + StrBuilder sb = new StrBuilder(); + sb.appendSeparator(','); // no effect + assertEquals("", sb.toString()); + sb.append("foo"); + assertEquals("foo", sb.toString()); + sb.appendSeparator(','); + assertEquals("foo,", sb.toString()); + } + public void testAppendSeparator_char_char() { + StrBuilder sb = new StrBuilder(); + final char startSeparator = ':'; + final char standardSeparator = ','; + final String foo = "foo"; + sb.appendSeparator(standardSeparator, startSeparator); // no effect + assertEquals(String.valueOf(startSeparator), sb.toString()); + sb.append(foo); + assertEquals(String.valueOf(startSeparator) + foo, sb.toString()); + sb.appendSeparator(standardSeparator, startSeparator); + assertEquals(String.valueOf(startSeparator) + foo + standardSeparator, sb.toString()); + } + + //----------------------------------------------------------------------- + public void testAppendSeparator_String_int() { + StrBuilder sb = new StrBuilder(); + sb.appendSeparator(",", 0); // no effect + assertEquals("", sb.toString()); + sb.append("foo"); + assertEquals("foo", sb.toString()); + sb.appendSeparator(",", 1); + assertEquals("foo,", sb.toString()); + + sb.appendSeparator(",", -1); // no effect + assertEquals("foo,", sb.toString()); + } + + //----------------------------------------------------------------------- + public void testAppendSeparator_char_int() { + StrBuilder sb = new StrBuilder(); + sb.appendSeparator(',', 0); // no effect + assertEquals("", sb.toString()); + sb.append("foo"); + assertEquals("foo", sb.toString()); + sb.appendSeparator(',', 1); + assertEquals("foo,", sb.toString()); + + sb.appendSeparator(',', -1); // no effect + assertEquals("foo,", sb.toString()); + } + + //----------------------------------------------------------------------- + public void testInsert() { + + StrBuilder sb = new StrBuilder(); + sb.append("barbaz"); + assertEquals("barbaz", sb.toString()); + + try { + sb.insert(-1, FOO); + fail("insert(-1, Object) expected StringIndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + sb.insert(7, FOO); + fail("insert(7, Object) expected StringIndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + + sb.insert(0, (Object) null); + assertEquals("barbaz", sb.toString()); + + sb.insert(0, FOO); + assertEquals("foobarbaz", sb.toString()); + + sb.clear(); + sb.append("barbaz"); + assertEquals("barbaz", sb.toString()); + + try { + sb.insert(-1, "foo"); + fail("insert(-1, String) expected StringIndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + sb.insert(7, "foo"); + fail("insert(7, String) expected StringIndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + + sb.insert(0, (String) null); + assertEquals("barbaz", sb.toString()); + + sb.insert(0, "foo"); + assertEquals("foobarbaz", sb.toString()); + + sb.clear(); + sb.append("barbaz"); + assertEquals("barbaz", sb.toString()); + + try { + sb.insert(-1, new char[]{'f', 'o', 'o'}); + fail("insert(-1, char[]) expected StringIndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + sb.insert(7, new char[]{'f', 'o', 'o'}); + fail("insert(7, char[]) expected StringIndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + + sb.insert(0, (char[]) null); + assertEquals("barbaz", sb.toString()); + + sb.insert(0, new char[0]); + assertEquals("barbaz", sb.toString()); + + sb.insert(0, new char[]{'f', 'o', 'o'}); + assertEquals("foobarbaz", sb.toString()); + + sb.clear(); + sb.append("barbaz"); + assertEquals("barbaz", sb.toString()); + + try { + sb.insert(-1, new char[]{'a', 'b', 'c', 'f', 'o', 'o', 'd', 'e', 'f'}, 3, 3); + fail("insert(-1, char[], 3, 3) expected StringIndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + sb.insert(7, new char[]{'a', 'b', 'c', 'f', 'o', 'o', 'd', 'e', 'f'}, 3, 3); + fail("insert(7, char[], 3, 3) expected StringIndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + + sb.insert(0, (char[]) null, 0, 0); + assertEquals("barbaz", sb.toString()); + + sb.insert(0, new char[0], 0, 0); + assertEquals("barbaz", sb.toString()); + + try { + sb.insert(0, new char[]{'a', 'b', 'c', 'f', 'o', 'o', 'd', 'e', 'f'}, -1, 3); + fail("insert(0, char[], -1, 3) expected StringIndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + sb.insert(0, new char[]{'a', 'b', 'c', 'f', 'o', 'o', 'd', 'e', 'f'}, 10, 3); + fail("insert(0, char[], 10, 3) expected StringIndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + sb.insert(0, new char[]{'a', 'b', 'c', 'f', 'o', 'o', 'd', 'e', 'f'}, 0, -1); + fail("insert(0, char[], 0, -1) expected StringIndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + sb.insert(0, new char[]{'a', 'b', 'c', 'f', 'o', 'o', 'd', 'e', 'f'}, 0, 10); + fail("insert(0, char[], 0, 10) expected StringIndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + + sb.insert(0, new char[]{'a', 'b', 'c', 'f', 'o', 'o', 'd', 'e', 'f'}, 0, 0); + assertEquals("barbaz", sb.toString()); + + sb.insert(0, new char[]{'a', 'b', 'c', 'f', 'o', 'o', 'd', 'e', 'f'}, 3, 3); + assertEquals("foobarbaz", sb.toString()); + + sb.clear(); + sb.append("barbaz"); + assertEquals("barbaz", sb.toString()); + + try { + sb.insert(-1, true); + fail("insert(-1, boolean) expected StringIndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + sb.insert(7, true); + fail("insert(7, boolean) expected StringIndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + + sb.insert(0, true); + assertEquals("truebarbaz", sb.toString()); + + sb.insert(0, false); + assertEquals("falsetruebarbaz", sb.toString()); + + sb.clear(); + sb.append("barbaz"); + assertEquals("barbaz", sb.toString()); + + try { + sb.insert(-1, '!'); + fail("insert(-1, char) expected StringIndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + sb.insert(7, '!'); + fail("insert(7, char) expected StringIndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + + sb.insert(0, '!'); + assertEquals("!barbaz", sb.toString()); + + sb.clear(); + sb.append("barbaz"); + assertEquals("barbaz", sb.toString()); + + try { + sb.insert(-1, 0); + fail("insert(-1, int) expected StringIndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + sb.insert(7, 0); + fail("insert(7, int) expected StringIndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + + sb.insert(0, '0'); + assertEquals("0barbaz", sb.toString()); + + sb.clear(); + sb.append("barbaz"); + assertEquals("barbaz", sb.toString()); + + try { + sb.insert(-1, 1L); + fail("insert(-1, long) expected StringIndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + sb.insert(7, 1L); + fail("insert(7, long) expected StringIndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + + sb.insert(0, 1L); + assertEquals("1barbaz", sb.toString()); + + sb.clear(); + sb.append("barbaz"); + assertEquals("barbaz", sb.toString()); + + try { + sb.insert(-1, 2.3F); + fail("insert(-1, float) expected StringIndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + sb.insert(7, 2.3F); + fail("insert(7, float) expected StringIndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + + sb.insert(0, 2.3F); + assertEquals("2.3barbaz", sb.toString()); + + sb.clear(); + sb.append("barbaz"); + assertEquals("barbaz", sb.toString()); + + try { + sb.insert(-1, 4.5D); + fail("insert(-1, double) expected StringIndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + sb.insert(7, 4.5D); + fail("insert(7, double) expected StringIndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + + sb.insert(0, 4.5D); + assertEquals("4.5barbaz", sb.toString()); + } + + //----------------------------------------------------------------------- + public void testInsertWithNullText() { + StrBuilder sb = new StrBuilder(); + sb.setNullText("null"); + sb.append("barbaz"); + assertEquals("barbaz", sb.toString()); + + try { + sb.insert(-1, FOO); + fail("insert(-1, Object) expected StringIndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + sb.insert(7, FOO); + fail("insert(7, Object) expected StringIndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + + sb.insert(0, (Object) null); + assertEquals("nullbarbaz", sb.toString()); + + sb.insert(0, FOO); + assertEquals("foonullbarbaz", sb.toString()); + + sb.clear(); + sb.append("barbaz"); + assertEquals("barbaz", sb.toString()); + + try { + sb.insert(-1, "foo"); + fail("insert(-1, String) expected StringIndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + + try { + sb.insert(7, "foo"); + fail("insert(7, String) expected StringIndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + + sb.insert(0, (String) null); + assertEquals("nullbarbaz", sb.toString()); + + sb.insert(0, "foo"); + assertEquals("foonullbarbaz", sb.toString()); + + sb.insert(0, (char[]) null); + assertEquals("nullfoonullbarbaz", sb.toString()); + + sb.insert(0, (char[]) null, 0, 0); + assertEquals("nullnullfoonullbarbaz", sb.toString()); + } +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/text/StrBuilderTest.java b/ApacheCommonsLang/org/apache/commons/lang3/text/StrBuilderTest.java new file mode 100644 index 0000000..f01487a --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/text/StrBuilderTest.java @@ -0,0 +1,1775 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.lang3.text; + +import java.io.Reader; +import java.io.Writer; +import java.util.Arrays; + +import junit.framework.TestCase; + +import org.apache.commons.lang3.ArrayUtils; + +/** + * Unit tests for {@link org.apache.commons.lang3.text.StrBuilder}. + * + * @version $Id: StrBuilderTest.java 1153484 2011-08-03 13:39:42Z ggregory $ + */ +public class StrBuilderTest extends TestCase { + + /** + * Create a new test case with the specified name. + * + * @param name + * name + */ + public StrBuilderTest(String name) { + super(name); + } + + //----------------------------------------------------------------------- + public void testConstructors() { + StrBuilder sb0 = new StrBuilder(); + assertEquals(32, sb0.capacity()); + assertEquals(0, sb0.length()); + assertEquals(0, sb0.size()); + + StrBuilder sb1 = new StrBuilder(32); + assertEquals(32, sb1.capacity()); + assertEquals(0, sb1.length()); + assertEquals(0, sb1.size()); + + StrBuilder sb2 = new StrBuilder(0); + assertEquals(32, sb2.capacity()); + assertEquals(0, sb2.length()); + assertEquals(0, sb2.size()); + + StrBuilder sb3 = new StrBuilder(-1); + assertEquals(32, sb3.capacity()); + assertEquals(0, sb3.length()); + assertEquals(0, sb3.size()); + + StrBuilder sb4 = new StrBuilder(1); + assertEquals(1, sb4.capacity()); + assertEquals(0, sb4.length()); + assertEquals(0, sb4.size()); + + StrBuilder sb5 = new StrBuilder((String) null); + assertEquals(32, sb5.capacity()); + assertEquals(0, sb5.length()); + assertEquals(0, sb5.size()); + + StrBuilder sb6 = new StrBuilder(""); + assertEquals(32, sb6.capacity()); + assertEquals(0, sb6.length()); + assertEquals(0, sb6.size()); + + StrBuilder sb7 = new StrBuilder("foo"); + assertEquals(35, sb7.capacity()); + assertEquals(3, sb7.length()); + assertEquals(3, sb7.size()); + } + + //----------------------------------------------------------------------- + public void testChaining() { + StrBuilder sb = new StrBuilder(); + assertSame(sb, sb.setNewLineText(null)); + assertSame(sb, sb.setNullText(null)); + assertSame(sb, sb.setLength(1)); + assertSame(sb, sb.setCharAt(0, 'a')); + assertSame(sb, sb.ensureCapacity(0)); + assertSame(sb, sb.minimizeCapacity()); + assertSame(sb, sb.clear()); + assertSame(sb, sb.reverse()); + assertSame(sb, sb.trim()); + } + + //----------------------------------------------------------------------- + public void testGetSetNewLineText() { + StrBuilder sb = new StrBuilder(); + assertEquals(null, sb.getNewLineText()); + + sb.setNewLineText("#"); + assertEquals("#", sb.getNewLineText()); + + sb.setNewLineText(""); + assertEquals("", sb.getNewLineText()); + + sb.setNewLineText((String) null); + assertEquals(null, sb.getNewLineText()); + } + + //----------------------------------------------------------------------- + public void testGetSetNullText() { + StrBuilder sb = new StrBuilder(); + assertEquals(null, sb.getNullText()); + + sb.setNullText("null"); + assertEquals("null", sb.getNullText()); + + sb.setNullText(""); + assertEquals(null, sb.getNullText()); + + sb.setNullText("NULL"); + assertEquals("NULL", sb.getNullText()); + + sb.setNullText((String) null); + assertEquals(null, sb.getNullText()); + } + + //----------------------------------------------------------------------- + public void testCapacityAndLength() { + StrBuilder sb = new StrBuilder(); + assertEquals(32, sb.capacity()); + assertEquals(0, sb.length()); + assertEquals(0, sb.size()); + assertTrue(sb.isEmpty()); + + sb.minimizeCapacity(); + assertEquals(0, sb.capacity()); + assertEquals(0, sb.length()); + assertEquals(0, sb.size()); + assertTrue(sb.isEmpty()); + + sb.ensureCapacity(32); + assertTrue(sb.capacity() >= 32); + assertEquals(0, sb.length()); + assertEquals(0, sb.size()); + assertTrue(sb.isEmpty()); + + sb.append("foo"); + assertTrue(sb.capacity() >= 32); + assertEquals(3, sb.length()); + assertEquals(3, sb.size()); + assertTrue(sb.isEmpty() == false); + + sb.clear(); + assertTrue(sb.capacity() >= 32); + assertEquals(0, sb.length()); + assertEquals(0, sb.size()); + assertTrue(sb.isEmpty()); + + sb.append("123456789012345678901234567890123"); + assertTrue(sb.capacity() > 32); + assertEquals(33, sb.length()); + assertEquals(33, sb.size()); + assertTrue(sb.isEmpty() == false); + + sb.ensureCapacity(16); + assertTrue(sb.capacity() > 16); + assertEquals(33, sb.length()); + assertEquals(33, sb.size()); + assertTrue(sb.isEmpty() == false); + + sb.minimizeCapacity(); + assertEquals(33, sb.capacity()); + assertEquals(33, sb.length()); + assertEquals(33, sb.size()); + assertTrue(sb.isEmpty() == false); + + try { + sb.setLength(-1); + fail("setLength(-1) expected StringIndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + + sb.setLength(33); + assertEquals(33, sb.capacity()); + assertEquals(33, sb.length()); + assertEquals(33, sb.size()); + assertTrue(sb.isEmpty() == false); + + sb.setLength(16); + assertTrue(sb.capacity() >= 16); + assertEquals(16, sb.length()); + assertEquals(16, sb.size()); + assertEquals("1234567890123456", sb.toString()); + assertTrue(sb.isEmpty() == false); + + sb.setLength(32); + assertTrue(sb.capacity() >= 32); + assertEquals(32, sb.length()); + assertEquals(32, sb.size()); + assertEquals("1234567890123456\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0", sb.toString()); + assertTrue(sb.isEmpty() == false); + + sb.setLength(0); + assertTrue(sb.capacity() >= 32); + assertEquals(0, sb.length()); + assertEquals(0, sb.size()); + assertTrue(sb.isEmpty()); + } + + //----------------------------------------------------------------------- + public void testLength() { + StrBuilder sb = new StrBuilder(); + assertEquals(0, sb.length()); + + sb.append("Hello"); + assertEquals(5, sb.length()); + } + + public void testSetLength() { + StrBuilder sb = new StrBuilder(); + sb.append("Hello"); + sb.setLength(2); // shorten + assertEquals("He", sb.toString()); + sb.setLength(2); // no change + assertEquals("He", sb.toString()); + sb.setLength(3); // lengthen + assertEquals("He\0", sb.toString()); + + try { + sb.setLength(-1); + fail("setLength(-1) expected StringIndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + //----------------------------------------------------------------------- + public void testCapacity() { + StrBuilder sb = new StrBuilder(); + assertEquals(sb.buffer.length, sb.capacity()); + + sb.append("HelloWorldHelloWorldHelloWorldHelloWorld"); + assertEquals(sb.buffer.length, sb.capacity()); + } + + public void testEnsureCapacity() { + StrBuilder sb = new StrBuilder(); + sb.ensureCapacity(2); + assertEquals(true, sb.capacity() >= 2); + + sb.ensureCapacity(-1); + assertEquals(true, sb.capacity() >= 0); + + sb.append("HelloWorld"); + sb.ensureCapacity(40); + assertEquals(true, sb.capacity() >= 40); + } + + public void testMinimizeCapacity() { + StrBuilder sb = new StrBuilder(); + sb.minimizeCapacity(); + assertEquals(0, sb.capacity()); + + sb.append("HelloWorld"); + sb.minimizeCapacity(); + assertEquals(10, sb.capacity()); + } + + //----------------------------------------------------------------------- + public void testSize() { + StrBuilder sb = new StrBuilder(); + assertEquals(0, sb.size()); + + sb.append("Hello"); + assertEquals(5, sb.size()); + } + + public void testIsEmpty() { + StrBuilder sb = new StrBuilder(); + assertEquals(true, sb.isEmpty()); + + sb.append("Hello"); + assertEquals(false, sb.isEmpty()); + + sb.clear(); + assertEquals(true, sb.isEmpty()); + } + + public void testClear() { + StrBuilder sb = new StrBuilder(); + sb.append("Hello"); + sb.clear(); + assertEquals(0, sb.length()); + assertEquals(true, sb.buffer.length >= 5); + } + + //----------------------------------------------------------------------- + public void testCharAt() { + StrBuilder sb = new StrBuilder(); + try { + sb.charAt(0); + fail("charAt(0) expected IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + try { + sb.charAt(-1); + fail("charAt(-1) expected IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + sb.append("foo"); + assertEquals('f', sb.charAt(0)); + assertEquals('o', sb.charAt(1)); + assertEquals('o', sb.charAt(2)); + try { + sb.charAt(-1); + fail("charAt(-1) expected IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + try { + sb.charAt(3); + fail("charAt(3) expected IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + } + + //----------------------------------------------------------------------- + public void testSetCharAt() { + StrBuilder sb = new StrBuilder(); + try { + sb.setCharAt(0, 'f'); + fail("setCharAt(0,) expected IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + try { + sb.setCharAt(-1, 'f'); + fail("setCharAt(-1,) expected IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + sb.append("foo"); + sb.setCharAt(0, 'b'); + sb.setCharAt(1, 'a'); + sb.setCharAt(2, 'r'); + try { + sb.setCharAt(3, '!'); + fail("setCharAt(3,) expected IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) { + // expected + } + assertEquals("bar", sb.toString()); + } + + //----------------------------------------------------------------------- + public void testDeleteCharAt() { + StrBuilder sb = new StrBuilder("abc"); + sb.deleteCharAt(0); + assertEquals("bc", sb.toString()); + + try { + sb.deleteCharAt(1000); + fail("Expected IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) {} + } + + //----------------------------------------------------------------------- + public void testToCharArray() { + StrBuilder sb = new StrBuilder(); + assertEquals(ArrayUtils.EMPTY_CHAR_ARRAY, sb.toCharArray()); + + char[] a = sb.toCharArray(); + assertNotNull("toCharArray() result is null", a); + assertEquals("toCharArray() result is too large", 0, a.length); + + sb.append("junit"); + a = sb.toCharArray(); + assertEquals("toCharArray() result incorrect length", 5, a.length); + assertTrue("toCharArray() result does not match", Arrays.equals("junit".toCharArray(), a)); + } + + public void testToCharArrayIntInt() { + StrBuilder sb = new StrBuilder(); + assertEquals(ArrayUtils.EMPTY_CHAR_ARRAY, sb.toCharArray(0, 0)); + + sb.append("junit"); + char[] a = sb.toCharArray(0, 20); // too large test + assertEquals("toCharArray(int,int) result incorrect length", 5, a.length); + assertTrue("toCharArray(int,int) result does not match", Arrays.equals("junit".toCharArray(), a)); + + a = sb.toCharArray(0, 4); + assertEquals("toCharArray(int,int) result incorrect length", 4, a.length); + assertTrue("toCharArray(int,int) result does not match", Arrays.equals("juni".toCharArray(), a)); + + a = sb.toCharArray(0, 4); + assertEquals("toCharArray(int,int) result incorrect length", 4, a.length); + assertTrue("toCharArray(int,int) result does not match", Arrays.equals("juni".toCharArray(), a)); + + a = sb.toCharArray(0, 1); + assertNotNull("toCharArray(int,int) result is null", a); + + try { + sb.toCharArray(-1, 5); + fail("no string index out of bound on -1"); + } catch (IndexOutOfBoundsException e) { + } + + try { + sb.toCharArray(6, 5); + fail("no string index out of bound on -1"); + } catch (IndexOutOfBoundsException e) { + } + } + + public void testGetChars ( ) { + StrBuilder sb = new StrBuilder(); + + char[] input = new char[10]; + char[] a = sb.getChars(input); + assertSame (input, a); + assertTrue(Arrays.equals(new char[10], a)); + + sb.append("junit"); + a = sb.getChars(input); + assertSame(input, a); + assertTrue(Arrays.equals(new char[] {'j','u','n','i','t',0,0,0,0,0},a)); + + a = sb.getChars(null); + assertNotSame(input,a); + assertEquals(5,a.length); + assertTrue(Arrays.equals("junit".toCharArray(),a)); + + input = new char[5]; + a = sb.getChars(input); + assertSame(input, a); + + input = new char[4]; + a = sb.getChars(input); + assertNotSame(input, a); + } + + public void testGetCharsIntIntCharArrayInt( ) { + StrBuilder sb = new StrBuilder(); + + sb.append("junit"); + char[] a = new char[5]; + sb.getChars(0,5,a,0); + assertTrue(Arrays.equals(new char[] {'j','u','n','i','t'},a)); + + a = new char[5]; + sb.getChars(0,2,a,3); + assertTrue(Arrays.equals(new char[] {0,0,0,'j','u'},a)); + + try { + sb.getChars(-1,0,a,0); + fail("no exception"); + } + catch (IndexOutOfBoundsException e) { + } + + try { + sb.getChars(0,-1,a,0); + fail("no exception"); + } + catch (IndexOutOfBoundsException e) { + } + + try { + sb.getChars(0,20,a,0); + fail("no exception"); + } + catch (IndexOutOfBoundsException e) { + } + + try { + sb.getChars(4,2,a,0); + fail("no exception"); + } + catch (IndexOutOfBoundsException e) { + } + } + + //----------------------------------------------------------------------- + public void testDeleteIntInt() { + StrBuilder sb = new StrBuilder("abc"); + sb.delete(0, 1); + assertEquals("bc", sb.toString()); + sb.delete(1, 2); + assertEquals("b", sb.toString()); + sb.delete(0, 1); + assertEquals("", sb.toString()); + sb.delete(0, 1000); + assertEquals("", sb.toString()); + + try { + sb.delete(1, 2); + fail("Expected IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) {} + try { + sb.delete(-1, 1); + fail("Expected IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) {} + + sb = new StrBuilder("anything"); + try { + sb.delete(2, 1); + fail("Expected IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) {} + } + + //----------------------------------------------------------------------- + public void testDeleteAll_char() { + StrBuilder sb = new StrBuilder("abcbccba"); + sb.deleteAll('X'); + assertEquals("abcbccba", sb.toString()); + sb.deleteAll('a'); + assertEquals("bcbccb", sb.toString()); + sb.deleteAll('c'); + assertEquals("bbb", sb.toString()); + sb.deleteAll('b'); + assertEquals("", sb.toString()); + + sb = new StrBuilder(""); + sb.deleteAll('b'); + assertEquals("", sb.toString()); + } + + public void testDeleteFirst_char() { + StrBuilder sb = new StrBuilder("abcba"); + sb.deleteFirst('X'); + assertEquals("abcba", sb.toString()); + sb.deleteFirst('a'); + assertEquals("bcba", sb.toString()); + sb.deleteFirst('c'); + assertEquals("bba", sb.toString()); + sb.deleteFirst('b'); + assertEquals("ba", sb.toString()); + + sb = new StrBuilder(""); + sb.deleteFirst('b'); + assertEquals("", sb.toString()); + } + + // ----------------------------------------------------------------------- + public void testDeleteAll_String() { + StrBuilder sb = new StrBuilder("abcbccba"); + sb.deleteAll((String) null); + assertEquals("abcbccba", sb.toString()); + sb.deleteAll(""); + assertEquals("abcbccba", sb.toString()); + + sb.deleteAll("X"); + assertEquals("abcbccba", sb.toString()); + sb.deleteAll("a"); + assertEquals("bcbccb", sb.toString()); + sb.deleteAll("c"); + assertEquals("bbb", sb.toString()); + sb.deleteAll("b"); + assertEquals("", sb.toString()); + + sb = new StrBuilder("abcbccba"); + sb.deleteAll("bc"); + assertEquals("acba", sb.toString()); + + sb = new StrBuilder(""); + sb.deleteAll("bc"); + assertEquals("", sb.toString()); + } + + public void testDeleteFirst_String() { + StrBuilder sb = new StrBuilder("abcbccba"); + sb.deleteFirst((String) null); + assertEquals("abcbccba", sb.toString()); + sb.deleteFirst(""); + assertEquals("abcbccba", sb.toString()); + + sb.deleteFirst("X"); + assertEquals("abcbccba", sb.toString()); + sb.deleteFirst("a"); + assertEquals("bcbccba", sb.toString()); + sb.deleteFirst("c"); + assertEquals("bbccba", sb.toString()); + sb.deleteFirst("b"); + assertEquals("bccba", sb.toString()); + + sb = new StrBuilder("abcbccba"); + sb.deleteFirst("bc"); + assertEquals("abccba", sb.toString()); + + sb = new StrBuilder(""); + sb.deleteFirst("bc"); + assertEquals("", sb.toString()); + } + + // ----------------------------------------------------------------------- + public void testDeleteAll_StrMatcher() { + StrBuilder sb = new StrBuilder("A0xA1A2yA3"); + sb.deleteAll((StrMatcher) null); + assertEquals("A0xA1A2yA3", sb.toString()); + sb.deleteAll(A_NUMBER_MATCHER); + assertEquals("xy", sb.toString()); + + sb = new StrBuilder("Ax1"); + sb.deleteAll(A_NUMBER_MATCHER); + assertEquals("Ax1", sb.toString()); + + sb = new StrBuilder(""); + sb.deleteAll(A_NUMBER_MATCHER); + assertEquals("", sb.toString()); + } + + public void testDeleteFirst_StrMatcher() { + StrBuilder sb = new StrBuilder("A0xA1A2yA3"); + sb.deleteFirst((StrMatcher) null); + assertEquals("A0xA1A2yA3", sb.toString()); + sb.deleteFirst(A_NUMBER_MATCHER); + assertEquals("xA1A2yA3", sb.toString()); + + sb = new StrBuilder("Ax1"); + sb.deleteFirst(A_NUMBER_MATCHER); + assertEquals("Ax1", sb.toString()); + + sb = new StrBuilder(""); + sb.deleteFirst(A_NUMBER_MATCHER); + assertEquals("", sb.toString()); + } + + // ----------------------------------------------------------------------- + public void testReplace_int_int_String() { + StrBuilder sb = new StrBuilder("abc"); + sb.replace(0, 1, "d"); + assertEquals("dbc", sb.toString()); + sb.replace(0, 1, "aaa"); + assertEquals("aaabc", sb.toString()); + sb.replace(0, 3, ""); + assertEquals("bc", sb.toString()); + sb.replace(1, 2, (String) null); + assertEquals("b", sb.toString()); + sb.replace(1, 1000, "text"); + assertEquals("btext", sb.toString()); + sb.replace(0, 1000, "text"); + assertEquals("text", sb.toString()); + + sb = new StrBuilder("atext"); + sb.replace(1, 1, "ny"); + assertEquals("anytext", sb.toString()); + try { + sb.replace(2, 1, "anything"); + fail("Expected IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) {} + + sb = new StrBuilder(); + try { + sb.replace(1, 2, "anything"); + fail("Expected IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) {} + try { + sb.replace(-1, 1, "anything"); + fail("Expected IndexOutOfBoundsException"); + } catch (IndexOutOfBoundsException e) {} + } + + //----------------------------------------------------------------------- + public void testReplaceAll_char_char() { + StrBuilder sb = new StrBuilder("abcbccba"); + sb.replaceAll('x', 'y'); + assertEquals("abcbccba", sb.toString()); + sb.replaceAll('a', 'd'); + assertEquals("dbcbccbd", sb.toString()); + sb.replaceAll('b', 'e'); + assertEquals("dececced", sb.toString()); + sb.replaceAll('c', 'f'); + assertEquals("defeffed", sb.toString()); + sb.replaceAll('d', 'd'); + assertEquals("defeffed", sb.toString()); + } + + //----------------------------------------------------------------------- + public void testReplaceFirst_char_char() { + StrBuilder sb = new StrBuilder("abcbccba"); + sb.replaceFirst('x', 'y'); + assertEquals("abcbccba", sb.toString()); + sb.replaceFirst('a', 'd'); + assertEquals("dbcbccba", sb.toString()); + sb.replaceFirst('b', 'e'); + assertEquals("decbccba", sb.toString()); + sb.replaceFirst('c', 'f'); + assertEquals("defbccba", sb.toString()); + sb.replaceFirst('d', 'd'); + assertEquals("defbccba", sb.toString()); + } + + //----------------------------------------------------------------------- + public void testReplaceAll_String_String() { + StrBuilder sb = new StrBuilder("abcbccba"); + sb.replaceAll((String) null, null); + assertEquals("abcbccba", sb.toString()); + sb.replaceAll((String) null, "anything"); + assertEquals("abcbccba", sb.toString()); + sb.replaceAll("", null); + assertEquals("abcbccba", sb.toString()); + sb.replaceAll("", "anything"); + assertEquals("abcbccba", sb.toString()); + + sb.replaceAll("x", "y"); + assertEquals("abcbccba", sb.toString()); + sb.replaceAll("a", "d"); + assertEquals("dbcbccbd", sb.toString()); + sb.replaceAll("d", null); + assertEquals("bcbccb", sb.toString()); + sb.replaceAll("cb", "-"); + assertEquals("b-c-", sb.toString()); + + sb = new StrBuilder("abcba"); + sb.replaceAll("b", "xbx"); + assertEquals("axbxcxbxa", sb.toString()); + + sb = new StrBuilder("bb"); + sb.replaceAll("b", "xbx"); + assertEquals("xbxxbx", sb.toString()); + } + + public void testReplaceFirst_String_String() { + StrBuilder sb = new StrBuilder("abcbccba"); + sb.replaceFirst((String) null, null); + assertEquals("abcbccba", sb.toString()); + sb.replaceFirst((String) null, "anything"); + assertEquals("abcbccba", sb.toString()); + sb.replaceFirst("", null); + assertEquals("abcbccba", sb.toString()); + sb.replaceFirst("", "anything"); + assertEquals("abcbccba", sb.toString()); + + sb.replaceFirst("x", "y"); + assertEquals("abcbccba", sb.toString()); + sb.replaceFirst("a", "d"); + assertEquals("dbcbccba", sb.toString()); + sb.replaceFirst("d", null); + assertEquals("bcbccba", sb.toString()); + sb.replaceFirst("cb", "-"); + assertEquals("b-ccba", sb.toString()); + + sb = new StrBuilder("abcba"); + sb.replaceFirst("b", "xbx"); + assertEquals("axbxcba", sb.toString()); + + sb = new StrBuilder("bb"); + sb.replaceFirst("b", "xbx"); + assertEquals("xbxb", sb.toString()); + } + + //----------------------------------------------------------------------- + public void testReplaceAll_StrMatcher_String() { + StrBuilder sb = new StrBuilder("abcbccba"); + sb.replaceAll((StrMatcher) null, null); + assertEquals("abcbccba", sb.toString()); + sb.replaceAll((StrMatcher) null, "anything"); + assertEquals("abcbccba", sb.toString()); + sb.replaceAll(StrMatcher.noneMatcher(), null); + assertEquals("abcbccba", sb.toString()); + sb.replaceAll(StrMatcher.noneMatcher(), "anything"); + assertEquals("abcbccba", sb.toString()); + + sb.replaceAll(StrMatcher.charMatcher('x'), "y"); + assertEquals("abcbccba", sb.toString()); + sb.replaceAll(StrMatcher.charMatcher('a'), "d"); + assertEquals("dbcbccbd", sb.toString()); + sb.replaceAll(StrMatcher.charMatcher('d'), null); + assertEquals("bcbccb", sb.toString()); + sb.replaceAll(StrMatcher.stringMatcher("cb"), "-"); + assertEquals("b-c-", sb.toString()); + + sb = new StrBuilder("abcba"); + sb.replaceAll(StrMatcher.charMatcher('b'), "xbx"); + assertEquals("axbxcxbxa", sb.toString()); + + sb = new StrBuilder("bb"); + sb.replaceAll(StrMatcher.charMatcher('b'), "xbx"); + assertEquals("xbxxbx", sb.toString()); + + sb = new StrBuilder("A1-A2A3-A4"); + sb.replaceAll(A_NUMBER_MATCHER, "***"); + assertEquals("***-******-***", sb.toString()); + } + + public void testReplaceFirst_StrMatcher_String() { + StrBuilder sb = new StrBuilder("abcbccba"); + sb.replaceFirst((StrMatcher) null, null); + assertEquals("abcbccba", sb.toString()); + sb.replaceFirst((StrMatcher) null, "anything"); + assertEquals("abcbccba", sb.toString()); + sb.replaceFirst(StrMatcher.noneMatcher(), null); + assertEquals("abcbccba", sb.toString()); + sb.replaceFirst(StrMatcher.noneMatcher(), "anything"); + assertEquals("abcbccba", sb.toString()); + + sb.replaceFirst(StrMatcher.charMatcher('x'), "y"); + assertEquals("abcbccba", sb.toString()); + sb.replaceFirst(StrMatcher.charMatcher('a'), "d"); + assertEquals("dbcbccba", sb.toString()); + sb.replaceFirst(StrMatcher.charMatcher('d'), null); + assertEquals("bcbccba", sb.toString()); + sb.replaceFirst(StrMatcher.stringMatcher("cb"), "-"); + assertEquals("b-ccba", sb.toString()); + + sb = new StrBuilder("abcba"); + sb.replaceFirst(StrMatcher.charMatcher('b'), "xbx"); + assertEquals("axbxcba", sb.toString()); + + sb = new StrBuilder("bb"); + sb.replaceFirst(StrMatcher.charMatcher('b'), "xbx"); + assertEquals("xbxb", sb.toString()); + + sb = new StrBuilder("A1-A2A3-A4"); + sb.replaceFirst(A_NUMBER_MATCHER, "***"); + assertEquals("***-A2A3-A4", sb.toString()); + } + + //----------------------------------------------------------------------- + public void testReplace_StrMatcher_String_int_int_int_VaryMatcher() { + StrBuilder sb = new StrBuilder("abcbccba"); + sb.replace((StrMatcher) null, "x", 0, sb.length(), -1); + assertEquals("abcbccba", sb.toString()); + + sb.replace(StrMatcher.charMatcher('a'), "x", 0, sb.length(), -1); + assertEquals("xbcbccbx", sb.toString()); + + sb.replace(StrMatcher.stringMatcher("cb"), "x", 0, sb.length(), -1); + assertEquals("xbxcxx", sb.toString()); + + sb = new StrBuilder("A1-A2A3-A4"); + sb.replace(A_NUMBER_MATCHER, "***", 0, sb.length(), -1); + assertEquals("***-******-***", sb.toString()); + + sb = new StrBuilder(); + sb.replace(A_NUMBER_MATCHER, "***", 0, sb.length(), -1); + assertEquals("", sb.toString()); + } + + public void testReplace_StrMatcher_String_int_int_int_VaryReplace() { + StrBuilder sb = new StrBuilder("abcbccba"); + sb.replace(StrMatcher.stringMatcher("cb"), "cb", 0, sb.length(), -1); + assertEquals("abcbccba", sb.toString()); + + sb = new StrBuilder("abcbccba"); + sb.replace(StrMatcher.stringMatcher("cb"), "-", 0, sb.length(), -1); + assertEquals("ab-c-a", sb.toString()); + + sb = new StrBuilder("abcbccba"); + sb.replace(StrMatcher.stringMatcher("cb"), "+++", 0, sb.length(), -1); + assertEquals("ab+++c+++a", sb.toString()); + + sb = new StrBuilder("abcbccba"); + sb.replace(StrMatcher.stringMatcher("cb"), "", 0, sb.length(), -1); + assertEquals("abca", sb.toString()); + + sb = new StrBuilder("abcbccba"); + sb.replace(StrMatcher.stringMatcher("cb"), null, 0, sb.length(), -1); + assertEquals("abca", sb.toString()); + } + + public void testReplace_StrMatcher_String_int_int_int_VaryStartIndex() { + StrBuilder sb = new StrBuilder("aaxaaaayaa"); + sb.replace(StrMatcher.stringMatcher("aa"), "-", 0, sb.length(), -1); + assertEquals("-x--y-", sb.toString()); + + sb = new StrBuilder("aaxaaaayaa"); + sb.replace(StrMatcher.stringMatcher("aa"), "-", 1, sb.length(), -1); + assertEquals("aax--y-", sb.toString()); + + sb = new StrBuilder("aaxaaaayaa"); + sb.replace(StrMatcher.stringMatcher("aa"), "-", 2, sb.length(), -1); + assertEquals("aax--y-", sb.toString()); + + sb = new StrBuilder("aaxaaaayaa"); + sb.replace(StrMatcher.stringMatcher("aa"), "-", 3, sb.length(), -1); + assertEquals("aax--y-", sb.toString()); + + sb = new StrBuilder("aaxaaaayaa"); + sb.replace(StrMatcher.stringMatcher("aa"), "-", 4, sb.length(), -1); + assertEquals("aaxa-ay-", sb.toString()); + + sb = new StrBuilder("aaxaaaayaa"); + sb.replace(StrMatcher.stringMatcher("aa"), "-", 5, sb.length(), -1); + assertEquals("aaxaa-y-", sb.toString()); + + sb = new StrBuilder("aaxaaaayaa"); + sb.replace(StrMatcher.stringMatcher("aa"), "-", 6, sb.length(), -1); + assertEquals("aaxaaaay-", sb.toString()); + + sb = new StrBuilder("aaxaaaayaa"); + sb.replace(StrMatcher.stringMatcher("aa"), "-", 7, sb.length(), -1); + assertEquals("aaxaaaay-", sb.toString()); + + sb = new StrBuilder("aaxaaaayaa"); + sb.replace(StrMatcher.stringMatcher("aa"), "-", 8, sb.length(), -1); + assertEquals("aaxaaaay-", sb.toString()); + + sb = new StrBuilder("aaxaaaayaa"); + sb.replace(StrMatcher.stringMatcher("aa"), "-", 9, sb.length(), -1); + assertEquals("aaxaaaayaa", sb.toString()); + + sb = new StrBuilder("aaxaaaayaa"); + sb.replace(StrMatcher.stringMatcher("aa"), "-", 10, sb.length(), -1); + assertEquals("aaxaaaayaa", sb.toString()); + + sb = new StrBuilder("aaxaaaayaa"); + try { + sb.replace(StrMatcher.stringMatcher("aa"), "-", 11, sb.length(), -1); + fail(); + } catch (IndexOutOfBoundsException ex) {} + assertEquals("aaxaaaayaa", sb.toString()); + + sb = new StrBuilder("aaxaaaayaa"); + try { + sb.replace(StrMatcher.stringMatcher("aa"), "-", -1, sb.length(), -1); + fail(); + } catch (IndexOutOfBoundsException ex) {} + assertEquals("aaxaaaayaa", sb.toString()); + } + + public void testReplace_StrMatcher_String_int_int_int_VaryEndIndex() { + StrBuilder sb = new StrBuilder("aaxaaaayaa"); + sb.replace(StrMatcher.stringMatcher("aa"), "-", 0, 0, -1); + assertEquals("aaxaaaayaa", sb.toString()); + + sb = new StrBuilder("aaxaaaayaa"); + sb.replace(StrMatcher.stringMatcher("aa"), "-", 0, 2, -1); + assertEquals("-xaaaayaa", sb.toString()); + + sb = new StrBuilder("aaxaaaayaa"); + sb.replace(StrMatcher.stringMatcher("aa"), "-", 0, 3, -1); + assertEquals("-xaaaayaa", sb.toString()); + + sb = new StrBuilder("aaxaaaayaa"); + sb.replace(StrMatcher.stringMatcher("aa"), "-", 0, 4, -1); + assertEquals("-xaaaayaa", sb.toString()); + + sb = new StrBuilder("aaxaaaayaa"); + sb.replace(StrMatcher.stringMatcher("aa"), "-", 0, 5, -1); + assertEquals("-x-aayaa", sb.toString()); + + sb = new StrBuilder("aaxaaaayaa"); + sb.replace(StrMatcher.stringMatcher("aa"), "-", 0, 6, -1); + assertEquals("-x-aayaa", sb.toString()); + + sb = new StrBuilder("aaxaaaayaa"); + sb.replace(StrMatcher.stringMatcher("aa"), "-", 0, 7, -1); + assertEquals("-x--yaa", sb.toString()); + + sb = new StrBuilder("aaxaaaayaa"); + sb.replace(StrMatcher.stringMatcher("aa"), "-", 0, 8, -1); + assertEquals("-x--yaa", sb.toString()); + + sb = new StrBuilder("aaxaaaayaa"); + sb.replace(StrMatcher.stringMatcher("aa"), "-", 0, 9, -1); + assertEquals("-x--yaa", sb.toString()); + + sb = new StrBuilder("aaxaaaayaa"); + sb.replace(StrMatcher.stringMatcher("aa"), "-", 0, 10, -1); + assertEquals("-x--y-", sb.toString()); + + sb = new StrBuilder("aaxaaaayaa"); + sb.replace(StrMatcher.stringMatcher("aa"), "-", 0, 1000, -1); + assertEquals("-x--y-", sb.toString()); + + sb = new StrBuilder("aaxaaaayaa"); + try { + sb.replace(StrMatcher.stringMatcher("aa"), "-", 2, 1, -1); + fail(); + } catch (IndexOutOfBoundsException ex) {} + assertEquals("aaxaaaayaa", sb.toString()); + } + + public void testReplace_StrMatcher_String_int_int_int_VaryCount() { + StrBuilder sb = new StrBuilder("aaxaaaayaa"); + sb.replace(StrMatcher.stringMatcher("aa"), "-", 0, 10, -1); + assertEquals("-x--y-", sb.toString()); + + sb = new StrBuilder("aaxaaaayaa"); + sb.replace(StrMatcher.stringMatcher("aa"), "-", 0, 10, 0); + assertEquals("aaxaaaayaa", sb.toString()); + + sb = new StrBuilder("aaxaaaayaa"); + sb.replace(StrMatcher.stringMatcher("aa"), "-", 0, 10, 1); + assertEquals("-xaaaayaa", sb.toString()); + + sb = new StrBuilder("aaxaaaayaa"); + sb.replace(StrMatcher.stringMatcher("aa"), "-", 0, 10, 2); + assertEquals("-x-aayaa", sb.toString()); + + sb = new StrBuilder("aaxaaaayaa"); + sb.replace(StrMatcher.stringMatcher("aa"), "-", 0, 10, 3); + assertEquals("-x--yaa", sb.toString()); + + sb = new StrBuilder("aaxaaaayaa"); + sb.replace(StrMatcher.stringMatcher("aa"), "-", 0, 10, 4); + assertEquals("-x--y-", sb.toString()); + + sb = new StrBuilder("aaxaaaayaa"); + sb.replace(StrMatcher.stringMatcher("aa"), "-", 0, 10, 5); + assertEquals("-x--y-", sb.toString()); + } + + //----------------------------------------------------------------------- + public void testReverse() { + StrBuilder sb = new StrBuilder(); + assertEquals("", sb.reverse().toString()); + + sb.clear().append(true); + assertEquals("eurt", sb.reverse().toString()); + assertEquals("true", sb.reverse().toString()); + } + + //----------------------------------------------------------------------- + public void testTrim() { + StrBuilder sb = new StrBuilder(); + assertEquals("", sb.reverse().toString()); + + sb.clear().append(" \u0000 "); + assertEquals("", sb.trim().toString()); + + sb.clear().append(" \u0000 a b c"); + assertEquals("a b c", sb.trim().toString()); + + sb.clear().append("a b c \u0000 "); + assertEquals("a b c", sb.trim().toString()); + + sb.clear().append(" \u0000 a b c \u0000 "); + assertEquals("a b c", sb.trim().toString()); + + sb.clear().append("a b c"); + assertEquals("a b c", sb.trim().toString()); + } + + //----------------------------------------------------------------------- + public void testStartsWith() { + StrBuilder sb = new StrBuilder(); + assertFalse(sb.startsWith("a")); + assertFalse(sb.startsWith(null)); + assertTrue(sb.startsWith("")); + sb.append("abc"); + assertTrue(sb.startsWith("a")); + assertTrue(sb.startsWith("ab")); + assertTrue(sb.startsWith("abc")); + assertFalse(sb.startsWith("cba")); + } + + public void testEndsWith() { + StrBuilder sb = new StrBuilder(); + assertFalse(sb.endsWith("a")); + assertFalse(sb.endsWith("c")); + assertTrue(sb.endsWith("")); + assertFalse(sb.endsWith(null)); + sb.append("abc"); + assertTrue(sb.endsWith("c")); + assertTrue(sb.endsWith("bc")); + assertTrue(sb.endsWith("abc")); + assertFalse(sb.endsWith("cba")); + assertFalse(sb.endsWith("abcd")); + assertFalse(sb.endsWith(" abc")); + assertFalse(sb.endsWith("abc ")); + } + + //----------------------------------------------------------------------- + public void testSubSequenceIntInt() { + StrBuilder sb = new StrBuilder ("hello goodbye"); + // Start index is negative + try { + sb.subSequence(-1, 5); + fail(); + } catch (IndexOutOfBoundsException e) {} + + // End index is negative + try { + sb.subSequence(2, -1); + fail(); + } catch (IndexOutOfBoundsException e) {} + + // End index greater than length() + try { + sb.subSequence(2, sb.length() + 1); + fail(); + } catch (IndexOutOfBoundsException e) {} + + // Start index greater then end index + try { + sb.subSequence(3, 2); + fail(); + } catch (IndexOutOfBoundsException e) {} + + // Normal cases + assertEquals ("hello", sb.subSequence(0, 5)); + assertEquals ("hello goodbye".subSequence(0, 6), sb.subSequence(0, 6)); + assertEquals ("goodbye", sb.subSequence(6, 13)); + assertEquals ("hello goodbye".subSequence(6,13), sb.subSequence(6, 13)); + } + + public void testSubstringInt() { + StrBuilder sb = new StrBuilder ("hello goodbye"); + assertEquals ("goodbye", sb.substring(6)); + assertEquals ("hello goodbye".substring(6), sb.substring(6)); + assertEquals ("hello goodbye", sb.substring(0)); + assertEquals ("hello goodbye".substring(0), sb.substring(0)); + try { + sb.substring(-1); + fail (); + } catch (IndexOutOfBoundsException e) {} + + try { + sb.substring(15); + fail (); + } catch (IndexOutOfBoundsException e) {} + + } + + public void testSubstringIntInt() { + StrBuilder sb = new StrBuilder ("hello goodbye"); + assertEquals ("hello", sb.substring(0, 5)); + assertEquals ("hello goodbye".substring(0, 6), sb.substring(0, 6)); + + assertEquals ("goodbye", sb.substring(6, 13)); + assertEquals ("hello goodbye".substring(6,13), sb.substring(6, 13)); + + assertEquals ("goodbye", sb.substring(6, 20)); + + try { + sb.substring(-1, 5); + fail(); + } catch (IndexOutOfBoundsException e) {} + + try { + sb.substring(15, 20); + fail(); + } catch (IndexOutOfBoundsException e) {} + } + + // ----------------------------------------------------------------------- + public void testMidString() { + StrBuilder sb = new StrBuilder("hello goodbye hello"); + assertEquals("goodbye", sb.midString(6, 7)); + assertEquals("hello", sb.midString(0, 5)); + assertEquals("hello", sb.midString(-5, 5)); + assertEquals("", sb.midString(0, -1)); + assertEquals("", sb.midString(20, 2)); + assertEquals("hello", sb.midString(14, 22)); + } + + public void testRightString() { + StrBuilder sb = new StrBuilder("left right"); + assertEquals("right", sb.rightString(5)); + assertEquals("", sb.rightString(0)); + assertEquals("", sb.rightString(-5)); + assertEquals("left right", sb.rightString(15)); + } + + public void testLeftString() { + StrBuilder sb = new StrBuilder("left right"); + assertEquals("left", sb.leftString(4)); + assertEquals("", sb.leftString(0)); + assertEquals("", sb.leftString(-5)); + assertEquals("left right", sb.leftString(15)); + } + + // ----------------------------------------------------------------------- + public void testContains_char() { + StrBuilder sb = new StrBuilder("abcdefghijklmnopqrstuvwxyz"); + assertEquals(true, sb.contains('a')); + assertEquals(true, sb.contains('o')); + assertEquals(true, sb.contains('z')); + assertEquals(false, sb.contains('1')); + } + + public void testContains_String() { + StrBuilder sb = new StrBuilder("abcdefghijklmnopqrstuvwxyz"); + assertEquals(true, sb.contains("a")); + assertEquals(true, sb.contains("pq")); + assertEquals(true, sb.contains("z")); + assertEquals(false, sb.contains("zyx")); + assertEquals(false, sb.contains((String) null)); + } + + public void testContains_StrMatcher() { + StrBuilder sb = new StrBuilder("abcdefghijklmnopqrstuvwxyz"); + assertEquals(true, sb.contains(StrMatcher.charMatcher('a'))); + assertEquals(true, sb.contains(StrMatcher.stringMatcher("pq"))); + assertEquals(true, sb.contains(StrMatcher.charMatcher('z'))); + assertEquals(false, sb.contains(StrMatcher.stringMatcher("zy"))); + assertEquals(false, sb.contains((StrMatcher) null)); + + sb = new StrBuilder(); + assertEquals(false, sb.contains(A_NUMBER_MATCHER)); + sb.append("B A1 C"); + assertEquals(true, sb.contains(A_NUMBER_MATCHER)); + } + + // ----------------------------------------------------------------------- + public void testIndexOf_char() { + StrBuilder sb = new StrBuilder("abab"); + assertEquals(0, sb.indexOf('a')); + + // should work like String#indexOf + assertEquals("abab".indexOf('a'), sb.indexOf('a')); + + assertEquals(1, sb.indexOf('b')); + assertEquals("abab".indexOf('b'), sb.indexOf('b')); + + assertEquals(-1, sb.indexOf('z')); + } + + public void testIndexOf_char_int() { + StrBuilder sb = new StrBuilder("abab"); + assertEquals(0, sb.indexOf('a', -1)); + assertEquals(0, sb.indexOf('a', 0)); + assertEquals(2, sb.indexOf('a', 1)); + assertEquals(-1, sb.indexOf('a', 4)); + assertEquals(-1, sb.indexOf('a', 5)); + + // should work like String#indexOf + assertEquals("abab".indexOf('a', 1), sb.indexOf('a', 1)); + + assertEquals(3, sb.indexOf('b', 2)); + assertEquals("abab".indexOf('b', 2), sb.indexOf('b', 2)); + + assertEquals(-1, sb.indexOf('z', 2)); + + sb = new StrBuilder("xyzabc"); + assertEquals(2, sb.indexOf('z', 0)); + assertEquals(-1, sb.indexOf('z', 3)); + } + + public void testLastIndexOf_char() { + StrBuilder sb = new StrBuilder("abab"); + + assertEquals (2, sb.lastIndexOf('a')); + //should work like String#lastIndexOf + assertEquals ("abab".lastIndexOf('a'), sb.lastIndexOf('a')); + + assertEquals(3, sb.lastIndexOf('b')); + assertEquals ("abab".lastIndexOf('b'), sb.lastIndexOf('b')); + + assertEquals (-1, sb.lastIndexOf('z')); + } + + public void testLastIndexOf_char_int() { + StrBuilder sb = new StrBuilder("abab"); + assertEquals(-1, sb.lastIndexOf('a', -1)); + assertEquals(0, sb.lastIndexOf('a', 0)); + assertEquals(0, sb.lastIndexOf('a', 1)); + + // should work like String#lastIndexOf + assertEquals("abab".lastIndexOf('a', 1), sb.lastIndexOf('a', 1)); + + assertEquals(1, sb.lastIndexOf('b', 2)); + assertEquals("abab".lastIndexOf('b', 2), sb.lastIndexOf('b', 2)); + + assertEquals(-1, sb.lastIndexOf('z', 2)); + + sb = new StrBuilder("xyzabc"); + assertEquals(2, sb.lastIndexOf('z', sb.length())); + assertEquals(-1, sb.lastIndexOf('z', 1)); + } + + // ----------------------------------------------------------------------- + public void testIndexOf_String() { + StrBuilder sb = new StrBuilder("abab"); + + assertEquals(0, sb.indexOf("a")); + //should work like String#indexOf + assertEquals("abab".indexOf("a"), sb.indexOf("a")); + + assertEquals(0, sb.indexOf("ab")); + //should work like String#indexOf + assertEquals("abab".indexOf("ab"), sb.indexOf("ab")); + + assertEquals(1, sb.indexOf("b")); + assertEquals("abab".indexOf("b"), sb.indexOf("b")); + + assertEquals(1, sb.indexOf("ba")); + assertEquals("abab".indexOf("ba"), sb.indexOf("ba")); + + assertEquals(-1, sb.indexOf("z")); + + assertEquals(-1, sb.indexOf((String) null)); + } + + public void testIndexOf_String_int() { + StrBuilder sb = new StrBuilder("abab"); + assertEquals(0, sb.indexOf("a", -1)); + assertEquals(0, sb.indexOf("a", 0)); + assertEquals(2, sb.indexOf("a", 1)); + assertEquals(2, sb.indexOf("a", 2)); + assertEquals(-1, sb.indexOf("a", 3)); + assertEquals(-1, sb.indexOf("a", 4)); + assertEquals(-1, sb.indexOf("a", 5)); + + assertEquals(-1, sb.indexOf("abcdef", 0)); + assertEquals(0, sb.indexOf("", 0)); + assertEquals(1, sb.indexOf("", 1)); + + //should work like String#indexOf + assertEquals ("abab".indexOf("a", 1), sb.indexOf("a", 1)); + + assertEquals(2, sb.indexOf("ab", 1)); + //should work like String#indexOf + assertEquals("abab".indexOf("ab", 1), sb.indexOf("ab", 1)); + + assertEquals(3, sb.indexOf("b", 2)); + assertEquals("abab".indexOf("b", 2), sb.indexOf("b", 2)); + + assertEquals(1, sb.indexOf("ba", 1)); + assertEquals("abab".indexOf("ba", 2), sb.indexOf("ba", 2)); + + assertEquals(-1, sb.indexOf("z", 2)); + + sb = new StrBuilder("xyzabc"); + assertEquals(2, sb.indexOf("za", 0)); + assertEquals(-1, sb.indexOf("za", 3)); + + assertEquals(-1, sb.indexOf((String) null, 2)); + } + + public void testLastIndexOf_String() { + StrBuilder sb = new StrBuilder("abab"); + + assertEquals(2, sb.lastIndexOf("a")); + //should work like String#lastIndexOf + assertEquals("abab".lastIndexOf("a"), sb.lastIndexOf("a")); + + assertEquals(2, sb.lastIndexOf("ab")); + //should work like String#lastIndexOf + assertEquals("abab".lastIndexOf("ab"), sb.lastIndexOf("ab")); + + assertEquals(3, sb.lastIndexOf("b")); + assertEquals("abab".lastIndexOf("b"), sb.lastIndexOf("b")); + + assertEquals(1, sb.lastIndexOf("ba")); + assertEquals("abab".lastIndexOf("ba"), sb.lastIndexOf("ba")); + + assertEquals(-1, sb.lastIndexOf("z")); + + assertEquals(-1, sb.lastIndexOf((String) null)); + } + + public void testLastIndexOf_String_int() { + StrBuilder sb = new StrBuilder("abab"); + assertEquals(-1, sb.lastIndexOf("a", -1)); + assertEquals(0, sb.lastIndexOf("a", 0)); + assertEquals(0, sb.lastIndexOf("a", 1)); + assertEquals(2, sb.lastIndexOf("a", 2)); + assertEquals(2, sb.lastIndexOf("a", 3)); + assertEquals(2, sb.lastIndexOf("a", 4)); + assertEquals(2, sb.lastIndexOf("a", 5)); + + assertEquals(-1, sb.lastIndexOf("abcdef", 3)); + assertEquals("abab".lastIndexOf("", 3), sb.lastIndexOf("", 3)); + assertEquals("abab".lastIndexOf("", 1), sb.lastIndexOf("", 1)); + + //should work like String#lastIndexOf + assertEquals("abab".lastIndexOf("a", 1), sb.lastIndexOf("a", 1)); + + assertEquals(0, sb.lastIndexOf("ab", 1)); + //should work like String#lastIndexOf + assertEquals("abab".lastIndexOf("ab", 1), sb.lastIndexOf("ab", 1)); + + assertEquals(1, sb.lastIndexOf("b", 2)); + assertEquals("abab".lastIndexOf("b", 2), sb.lastIndexOf("b", 2)); + + assertEquals(1, sb.lastIndexOf("ba", 2)); + assertEquals("abab".lastIndexOf("ba", 2), sb.lastIndexOf("ba", 2)); + + assertEquals(-1, sb.lastIndexOf("z", 2)); + + sb = new StrBuilder("xyzabc"); + assertEquals(2, sb.lastIndexOf("za", sb.length())); + assertEquals(-1, sb.lastIndexOf("za", 1)); + + assertEquals(-1, sb.lastIndexOf((String) null, 2)); + } + + // ----------------------------------------------------------------------- + public void testIndexOf_StrMatcher() { + StrBuilder sb = new StrBuilder(); + assertEquals(-1, sb.indexOf((StrMatcher) null)); + assertEquals(-1, sb.indexOf(StrMatcher.charMatcher('a'))); + + sb.append("ab bd"); + assertEquals(0, sb.indexOf(StrMatcher.charMatcher('a'))); + assertEquals(1, sb.indexOf(StrMatcher.charMatcher('b'))); + assertEquals(2, sb.indexOf(StrMatcher.spaceMatcher())); + assertEquals(4, sb.indexOf(StrMatcher.charMatcher('d'))); + assertEquals(-1, sb.indexOf(StrMatcher.noneMatcher())); + assertEquals(-1, sb.indexOf((StrMatcher) null)); + + sb.append(" A1 junction"); + assertEquals(6, sb.indexOf(A_NUMBER_MATCHER)); + } + + public void testIndexOf_StrMatcher_int() { + StrBuilder sb = new StrBuilder(); + assertEquals(-1, sb.indexOf((StrMatcher) null, 2)); + assertEquals(-1, sb.indexOf(StrMatcher.charMatcher('a'), 2)); + assertEquals(-1, sb.indexOf(StrMatcher.charMatcher('a'), 0)); + + sb.append("ab bd"); + assertEquals(0, sb.indexOf(StrMatcher.charMatcher('a'), -2)); + assertEquals(0, sb.indexOf(StrMatcher.charMatcher('a'), 0)); + assertEquals(-1, sb.indexOf(StrMatcher.charMatcher('a'), 2)); + assertEquals(-1, sb.indexOf(StrMatcher.charMatcher('a'), 20)); + + assertEquals(1, sb.indexOf(StrMatcher.charMatcher('b'), -1)); + assertEquals(1, sb.indexOf(StrMatcher.charMatcher('b'), 0)); + assertEquals(1, sb.indexOf(StrMatcher.charMatcher('b'), 1)); + assertEquals(3, sb.indexOf(StrMatcher.charMatcher('b'), 2)); + assertEquals(3, sb.indexOf(StrMatcher.charMatcher('b'), 3)); + assertEquals(-1, sb.indexOf(StrMatcher.charMatcher('b'), 4)); + assertEquals(-1, sb.indexOf(StrMatcher.charMatcher('b'), 5)); + assertEquals(-1, sb.indexOf(StrMatcher.charMatcher('b'), 6)); + + assertEquals(2, sb.indexOf(StrMatcher.spaceMatcher(), -2)); + assertEquals(2, sb.indexOf(StrMatcher.spaceMatcher(), 0)); + assertEquals(2, sb.indexOf(StrMatcher.spaceMatcher(), 2)); + assertEquals(-1, sb.indexOf(StrMatcher.spaceMatcher(), 4)); + assertEquals(-1, sb.indexOf(StrMatcher.spaceMatcher(), 20)); + + assertEquals(-1, sb.indexOf(StrMatcher.noneMatcher(), 0)); + assertEquals(-1, sb.indexOf((StrMatcher) null, 0)); + + sb.append(" A1 junction with A2"); + assertEquals(6, sb.indexOf(A_NUMBER_MATCHER, 5)); + assertEquals(6, sb.indexOf(A_NUMBER_MATCHER, 6)); + assertEquals(23, sb.indexOf(A_NUMBER_MATCHER, 7)); + assertEquals(23, sb.indexOf(A_NUMBER_MATCHER, 22)); + assertEquals(23, sb.indexOf(A_NUMBER_MATCHER, 23)); + assertEquals(-1, sb.indexOf(A_NUMBER_MATCHER, 24)); + } + + public void testLastIndexOf_StrMatcher() { + StrBuilder sb = new StrBuilder(); + assertEquals(-1, sb.lastIndexOf((StrMatcher) null)); + assertEquals(-1, sb.lastIndexOf(StrMatcher.charMatcher('a'))); + + sb.append("ab bd"); + assertEquals(0, sb.lastIndexOf(StrMatcher.charMatcher('a'))); + assertEquals(3, sb.lastIndexOf(StrMatcher.charMatcher('b'))); + assertEquals(2, sb.lastIndexOf(StrMatcher.spaceMatcher())); + assertEquals(4, sb.lastIndexOf(StrMatcher.charMatcher('d'))); + assertEquals(-1, sb.lastIndexOf(StrMatcher.noneMatcher())); + assertEquals(-1, sb.lastIndexOf((StrMatcher) null)); + + sb.append(" A1 junction"); + assertEquals(6, sb.lastIndexOf(A_NUMBER_MATCHER)); + } + + public void testLastIndexOf_StrMatcher_int() { + StrBuilder sb = new StrBuilder(); + assertEquals(-1, sb.lastIndexOf((StrMatcher) null, 2)); + assertEquals(-1, sb.lastIndexOf(StrMatcher.charMatcher('a'), 2)); + assertEquals(-1, sb.lastIndexOf(StrMatcher.charMatcher('a'), 0)); + assertEquals(-1, sb.lastIndexOf(StrMatcher.charMatcher('a'), -1)); + + sb.append("ab bd"); + assertEquals(-1, sb.lastIndexOf(StrMatcher.charMatcher('a'), -2)); + assertEquals(0, sb.lastIndexOf(StrMatcher.charMatcher('a'), 0)); + assertEquals(0, sb.lastIndexOf(StrMatcher.charMatcher('a'), 2)); + assertEquals(0, sb.lastIndexOf(StrMatcher.charMatcher('a'), 20)); + + assertEquals(-1, sb.lastIndexOf(StrMatcher.charMatcher('b'), -1)); + assertEquals(-1, sb.lastIndexOf(StrMatcher.charMatcher('b'), 0)); + assertEquals(1, sb.lastIndexOf(StrMatcher.charMatcher('b'), 1)); + assertEquals(1, sb.lastIndexOf(StrMatcher.charMatcher('b'), 2)); + assertEquals(3, sb.lastIndexOf(StrMatcher.charMatcher('b'), 3)); + assertEquals(3, sb.lastIndexOf(StrMatcher.charMatcher('b'), 4)); + assertEquals(3, sb.lastIndexOf(StrMatcher.charMatcher('b'), 5)); + assertEquals(3, sb.lastIndexOf(StrMatcher.charMatcher('b'), 6)); + + assertEquals(-1, sb.lastIndexOf(StrMatcher.spaceMatcher(), -2)); + assertEquals(-1, sb.lastIndexOf(StrMatcher.spaceMatcher(), 0)); + assertEquals(2, sb.lastIndexOf(StrMatcher.spaceMatcher(), 2)); + assertEquals(2, sb.lastIndexOf(StrMatcher.spaceMatcher(), 4)); + assertEquals(2, sb.lastIndexOf(StrMatcher.spaceMatcher(), 20)); + + assertEquals(-1, sb.lastIndexOf(StrMatcher.noneMatcher(), 0)); + assertEquals(-1, sb.lastIndexOf((StrMatcher) null, 0)); + + sb.append(" A1 junction with A2"); + assertEquals(-1, sb.lastIndexOf(A_NUMBER_MATCHER, 5)); + assertEquals(-1, sb.lastIndexOf(A_NUMBER_MATCHER, 6)); // A matches, 1 is outside bounds + assertEquals(6, sb.lastIndexOf(A_NUMBER_MATCHER, 7)); + assertEquals(6, sb.lastIndexOf(A_NUMBER_MATCHER, 22)); + assertEquals(6, sb.lastIndexOf(A_NUMBER_MATCHER, 23)); // A matches, 2 is outside bounds + assertEquals(23, sb.lastIndexOf(A_NUMBER_MATCHER, 24)); + } + + static final StrMatcher A_NUMBER_MATCHER = new StrMatcher() { + @Override + public int isMatch(char[] buffer, int pos, int bufferStart, int bufferEnd) { + if (buffer[pos] == 'A') { + pos++; + if (pos < bufferEnd && buffer[pos] >= '0' && buffer[pos] <= '9') { + return 2; + } + } + return 0; + } + }; + + //----------------------------------------------------------------------- + public void testAsTokenizer() throws Exception { + // from Javadoc + StrBuilder b = new StrBuilder(); + b.append("a b "); + StrTokenizer t = b.asTokenizer(); + + String[] tokens1 = t.getTokenArray(); + assertEquals(2, tokens1.length); + assertEquals("a", tokens1[0]); + assertEquals("b", tokens1[1]); + assertEquals(2, t.size()); + + b.append("c d "); + String[] tokens2 = t.getTokenArray(); + assertEquals(2, tokens2.length); + assertEquals("a", tokens2[0]); + assertEquals("b", tokens2[1]); + assertEquals(2, t.size()); + assertEquals("a", t.next()); + assertEquals("b", t.next()); + + t.reset(); + String[] tokens3 = t.getTokenArray(); + assertEquals(4, tokens3.length); + assertEquals("a", tokens3[0]); + assertEquals("b", tokens3[1]); + assertEquals("c", tokens3[2]); + assertEquals("d", tokens3[3]); + assertEquals(4, t.size()); + assertEquals("a", t.next()); + assertEquals("b", t.next()); + assertEquals("c", t.next()); + assertEquals("d", t.next()); + + assertEquals("a b c d ", t.getContent()); + } + + // ----------------------------------------------------------------------- + public void testAsReader() throws Exception { + StrBuilder sb = new StrBuilder("some text"); + Reader reader = sb.asReader(); + assertEquals(true, reader.ready()); + char[] buf = new char[40]; + assertEquals(9, reader.read(buf)); + assertEquals("some text", new String(buf, 0, 9)); + + assertEquals(-1, reader.read()); + assertEquals(false, reader.ready()); + assertEquals(0, reader.skip(2)); + assertEquals(0, reader.skip(-1)); + + assertEquals(true, reader.markSupported()); + reader = sb.asReader(); + assertEquals('s', reader.read()); + reader.mark(-1); + char[] array = new char[3]; + assertEquals(3, reader.read(array, 0, 3)); + assertEquals('o', array[0]); + assertEquals('m', array[1]); + assertEquals('e', array[2]); + reader.reset(); + assertEquals(1, reader.read(array, 1, 1)); + assertEquals('o', array[0]); + assertEquals('o', array[1]); + assertEquals('e', array[2]); + assertEquals(2, reader.skip(2)); + assertEquals(' ', reader.read()); + + assertEquals(true, reader.ready()); + reader.close(); + assertEquals(true, reader.ready()); + + reader = sb.asReader(); + array = new char[3]; + try { + reader.read(array, -1, 0); + fail(); + } catch (IndexOutOfBoundsException ex) {} + try { + reader.read(array, 0, -1); + fail(); + } catch (IndexOutOfBoundsException ex) {} + try { + reader.read(array, 100, 1); + fail(); + } catch (IndexOutOfBoundsException ex) {} + try { + reader.read(array, 0, 100); + fail(); + } catch (IndexOutOfBoundsException ex) {} + try { + reader.read(array, Integer.MAX_VALUE, Integer.MAX_VALUE); + fail(); + } catch (IndexOutOfBoundsException ex) {} + + assertEquals(0, reader.read(array, 0, 0)); + assertEquals(0, array[0]); + assertEquals(0, array[1]); + assertEquals(0, array[2]); + + reader.skip(9); + assertEquals(-1, reader.read(array, 0, 1)); + + reader.reset(); + array = new char[30]; + assertEquals(9, reader.read(array, 0, 30)); + } + + //----------------------------------------------------------------------- + public void testAsWriter() throws Exception { + StrBuilder sb = new StrBuilder("base"); + Writer writer = sb.asWriter(); + + writer.write('l'); + assertEquals("basel", sb.toString()); + + writer.write(new char[] {'i', 'n'}); + assertEquals("baselin", sb.toString()); + + writer.write(new char[] {'n', 'e', 'r'}, 1, 2); + assertEquals("baseliner", sb.toString()); + + writer.write(" rout"); + assertEquals("baseliner rout", sb.toString()); + + writer.write("ping that server", 1, 3); + assertEquals("baseliner routing", sb.toString()); + + writer.flush(); // no effect + assertEquals("baseliner routing", sb.toString()); + + writer.close(); // no effect + assertEquals("baseliner routing", sb.toString()); + + writer.write(" hi"); // works after close + assertEquals("baseliner routing hi", sb.toString()); + + sb.setLength(4); // mix and match + writer.write('d'); + assertEquals("based", sb.toString()); + } + + //----------------------------------------------------------------------- + public void testEqualsIgnoreCase() { + StrBuilder sb1 = new StrBuilder(); + StrBuilder sb2 = new StrBuilder(); + assertEquals(true, sb1.equalsIgnoreCase(sb1)); + assertEquals(true, sb1.equalsIgnoreCase(sb2)); + assertEquals(true, sb2.equalsIgnoreCase(sb2)); + + sb1.append("abc"); + assertEquals(false, sb1.equalsIgnoreCase(sb2)); + + sb2.append("ABC"); + assertEquals(true, sb1.equalsIgnoreCase(sb2)); + + sb2.clear().append("abc"); + assertEquals(true, sb1.equalsIgnoreCase(sb2)); + assertEquals(true, sb1.equalsIgnoreCase(sb1)); + assertEquals(true, sb2.equalsIgnoreCase(sb2)); + + sb2.clear().append("aBc"); + assertEquals(true, sb1.equalsIgnoreCase(sb2)); + } + + //----------------------------------------------------------------------- + public void testEquals() { + StrBuilder sb1 = new StrBuilder(); + StrBuilder sb2 = new StrBuilder(); + assertEquals(true, sb1.equals(sb2)); + assertEquals(true, sb1.equals(sb1)); + assertEquals(true, sb2.equals(sb2)); + assertEquals(true, sb1.equals((Object) sb2)); + + sb1.append("abc"); + assertEquals(false, sb1.equals(sb2)); + assertEquals(false, sb1.equals((Object) sb2)); + + sb2.append("ABC"); + assertEquals(false, sb1.equals(sb2)); + assertEquals(false, sb1.equals((Object) sb2)); + + sb2.clear().append("abc"); + assertEquals(true, sb1.equals(sb2)); + assertEquals(true, sb1.equals((Object) sb2)); + + assertEquals(false, sb1.equals(Integer.valueOf(1))); + assertEquals(false, sb1.equals("abc")); + } + + //----------------------------------------------------------------------- + public void testHashCode() { + StrBuilder sb = new StrBuilder(); + int hc1a = sb.hashCode(); + int hc1b = sb.hashCode(); + assertEquals(0, hc1a); + assertEquals(hc1a, hc1b); + + sb.append("abc"); + int hc2a = sb.hashCode(); + int hc2b = sb.hashCode(); + assertEquals(true, hc2a != 0); + assertEquals(hc2a, hc2b); + } + + //----------------------------------------------------------------------- + public void testToString() { + StrBuilder sb = new StrBuilder("abc"); + assertEquals("abc", sb.toString()); + } + + //----------------------------------------------------------------------- + public void testToStringBuffer() { + StrBuilder sb = new StrBuilder(); + assertEquals(new StringBuffer().toString(), sb.toStringBuffer().toString()); + + sb.append("junit"); + assertEquals(new StringBuffer("junit").toString(), sb.toStringBuffer().toString()); + } + + //----------------------------------------------------------------------- + public void testLang294() { + StrBuilder sb = new StrBuilder("\n%BLAH%\nDo more stuff\neven more stuff\n%BLAH%\n"); + sb.deleteAll("\n%BLAH%"); + assertEquals("\nDo more stuff\neven more stuff\n", sb.toString()); + } + + public void testIndexOfLang294() { + StrBuilder sb = new StrBuilder("onetwothree"); + sb.deleteFirst("three"); + assertEquals(-1, sb.indexOf("three")); + } + + //----------------------------------------------------------------------- + public void testLang295() { + StrBuilder sb = new StrBuilder("onetwothree"); + sb.deleteFirst("three"); + assertFalse( "The contains(char) method is looking beyond the end of the string", sb.contains('h')); + assertEquals( "The indexOf(char) method is looking beyond the end of the string", -1, sb.indexOf('h')); + } + + //----------------------------------------------------------------------- + public void testLang412Right() { + StrBuilder sb = new StrBuilder(); + sb.appendFixedWidthPadRight(null, 10, '*'); + assertEquals( "Failed to invoke appendFixedWidthPadRight correctly", "**********", sb.toString()); + } + + public void testLang412Left() { + StrBuilder sb = new StrBuilder(); + sb.appendFixedWidthPadLeft(null, 10, '*'); + assertEquals( "Failed to invoke appendFixedWidthPadLeft correctly", "**********", sb.toString()); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/text/StrLookup.java b/ApacheCommonsLang/org/apache/commons/lang3/text/StrLookup.java new file mode 100644 index 0000000..801cd69 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/text/StrLookup.java @@ -0,0 +1,172 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.text; + +import java.util.Map; + +/** + * Lookup a String key to a String value. + *

    + * This class represents the simplest form of a string to string map. + * It has a benefit over a map in that it can create the result on + * demand based on the key. + *

    + * This class comes complete with various factory methods. + * If these do not suffice, you can subclass and implement your own matcher. + *

    + * For example, it would be possible to implement a lookup that used the + * key as a primary key, and looked up the value on demand from the database + * + * @since 2.2 + * @version $Id: StrLookup.java 1153484 2011-08-03 13:39:42Z ggregory $ + */ +public abstract class StrLookup { + + /** + * Lookup that always returns null. + */ + private static final StrLookup NONE_LOOKUP; + /** + * Lookup that uses System properties. + */ + private static final StrLookup SYSTEM_PROPERTIES_LOOKUP; + static { + NONE_LOOKUP = new MapStrLookup(null); + StrLookup lookup = null; + try { + final Map propMap = System.getProperties(); + @SuppressWarnings("unchecked") // System property keys and values are always Strings + final Map properties = (Map) propMap; + lookup = new MapStrLookup(properties); + } catch (SecurityException ex) { + lookup = NONE_LOOKUP; + } + SYSTEM_PROPERTIES_LOOKUP = lookup; + } + + //----------------------------------------------------------------------- + /** + * Returns a lookup which always returns null. + * + * @return a lookup that always returns null, not null + */ + public static StrLookup noneLookup() { + return NONE_LOOKUP; + } + + /** + * Returns a lookup which uses {@link System#getProperties() System properties} + * to lookup the key to value. + *

    + * If a security manager blocked access to system properties, then null will + * be returned from every lookup. + *

    + * If a null key is used, this lookup will throw a NullPointerException. + * + * @return a lookup using system properties, not null + */ + public static StrLookup systemPropertiesLookup() { + return SYSTEM_PROPERTIES_LOOKUP; + } + + /** + * Returns a lookup which looks up values using a map. + *

    + * If the map is null, then null will be returned from every lookup. + * The map result object is converted to a string using toString(). + * + * @param the type of the values supported by the lookup + * @param map the map of keys to values, may be null + * @return a lookup using the map, not null + */ + public static StrLookup mapLookup(Map map) { + return new MapStrLookup(map); + } + + //----------------------------------------------------------------------- + /** + * Constructor. + */ + protected StrLookup() { + super(); + } + + /** + * Looks up a String key to a String value. + *

    + * The internal implementation may use any mechanism to return the value. + * The simplest implementation is to use a Map. However, virtually any + * implementation is possible. + *

    + * For example, it would be possible to implement a lookup that used the + * key as a primary key, and looked up the value on demand from the database + * Or, a numeric based implementation could be created that treats the key + * as an integer, increments the value and return the result as a string - + * converting 1 to 2, 15 to 16 etc. + *

    + * The {@link #lookup(String)} method always returns a String, regardless of + * the underlying data, by converting it as necessary. For example: + *

    +     * Map map = new HashMap();
    +     * map.put("number", Integer.valueOf(2));
    +     * assertEquals("2", StrLookup.mapLookup(map).lookup("number"));
    +     * 
    + * @param key the key to be looked up, may be null + * @return the matching value, null if no match + */ + public abstract String lookup(String key); + + //----------------------------------------------------------------------- + /** + * Lookup implementation that uses a Map. + */ + static class MapStrLookup extends StrLookup { + + /** Map keys are variable names and value. */ + private final Map map; + + /** + * Creates a new instance backed by a Map. + * + * @param map the map of keys to values, may be null + */ + MapStrLookup(Map map) { + this.map = map; + } + + /** + * Looks up a String key to a String value using the map. + *

    + * If the map is null, then null is returned. + * The map result object is converted to a string using toString(). + * + * @param key the key to be looked up, may be null + * @return the matching value, null if no match + */ + @Override + public String lookup(String key) { + if (map == null) { + return null; + } + Object obj = map.get(key); + if (obj == null) { + return null; + } + return obj.toString(); + } + } +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/text/StrLookupTest.java b/ApacheCommonsLang/org/apache/commons/lang3/text/StrLookupTest.java new file mode 100644 index 0000000..b1ef1d8 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/text/StrLookupTest.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.lang3.text; + +import java.util.HashMap; +import java.util.Map; + +import junit.framework.TestCase; + +/** + * Test class for StrLookup. + * + * @version $Id: StrLookupTest.java 1153484 2011-08-03 13:39:42Z ggregory $ + */ +public class StrLookupTest extends TestCase { + + //----------------------------------------------------------------------- + public void testNoneLookup() { + assertEquals(null, StrLookup.noneLookup().lookup(null)); + assertEquals(null, StrLookup.noneLookup().lookup("")); + assertEquals(null, StrLookup.noneLookup().lookup("any")); + } + + public void testSystemProperiesLookup() { + assertEquals(System.getProperty("os.name"), StrLookup.systemPropertiesLookup().lookup("os.name")); + assertEquals(null, StrLookup.systemPropertiesLookup().lookup("")); + assertEquals(null, StrLookup.systemPropertiesLookup().lookup("other")); + try { + StrLookup.systemPropertiesLookup().lookup(null); + fail(); + } catch (NullPointerException ex) { + // expected + } + } + + public void testMapLookup() { + Map map = new HashMap(); + map.put("key", "value"); + map.put("number", Integer.valueOf(2)); + assertEquals("value", StrLookup.mapLookup(map).lookup("key")); + assertEquals("2", StrLookup.mapLookup(map).lookup("number")); + assertEquals(null, StrLookup.mapLookup(map).lookup(null)); + assertEquals(null, StrLookup.mapLookup(map).lookup("")); + assertEquals(null, StrLookup.mapLookup(map).lookup("other")); + } + + public void testMapLookup_nullMap() { + Map map = null; + assertEquals(null, StrLookup.mapLookup(map).lookup(null)); + assertEquals(null, StrLookup.mapLookup(map).lookup("")); + assertEquals(null, StrLookup.mapLookup(map).lookup("any")); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/text/StrMatcher.java b/ApacheCommonsLang/org/apache/commons/lang3/text/StrMatcher.java new file mode 100644 index 0000000..a88887e --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/text/StrMatcher.java @@ -0,0 +1,436 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.text; + +import java.util.Arrays; + +import org.apache.commons.lang3.StringUtils; + +/** + * A matcher class that can be queried to determine if a character array + * portion matches. + *

    + * This class comes complete with various factory methods. + * If these do not suffice, you can subclass and implement your own matcher. + * + * @since 2.2 + * @version $Id: StrMatcher.java 1299411 2012-03-11 17:55:29Z ggregory $ + */ +public abstract class StrMatcher { + + /** + * Matches the comma character. + */ + private static final StrMatcher COMMA_MATCHER = new CharMatcher(','); + /** + * Matches the tab character. + */ + private static final StrMatcher TAB_MATCHER = new CharMatcher('\t'); + /** + * Matches the space character. + */ + private static final StrMatcher SPACE_MATCHER = new CharMatcher(' '); + /** + * Matches the same characters as StringTokenizer, + * namely space, tab, newline, formfeed. + */ + private static final StrMatcher SPLIT_MATCHER = new CharSetMatcher(" \t\n\r\f".toCharArray()); + /** + * Matches the String trim() whitespace characters. + */ + private static final StrMatcher TRIM_MATCHER = new TrimMatcher(); + /** + * Matches the double quote character. + */ + private static final StrMatcher SINGLE_QUOTE_MATCHER = new CharMatcher('\''); + /** + * Matches the double quote character. + */ + private static final StrMatcher DOUBLE_QUOTE_MATCHER = new CharMatcher('"'); + /** + * Matches the single or double quote character. + */ + private static final StrMatcher QUOTE_MATCHER = new CharSetMatcher("'\"".toCharArray()); + /** + * Matches no characters. + */ + private static final StrMatcher NONE_MATCHER = new NoMatcher(); + + // ----------------------------------------------------------------------- + + /** + * Returns a matcher which matches the comma character. + * + * @return a matcher for a comma + */ + public static StrMatcher commaMatcher() { + return COMMA_MATCHER; + } + + /** + * Returns a matcher which matches the tab character. + * + * @return a matcher for a tab + */ + public static StrMatcher tabMatcher() { + return TAB_MATCHER; + } + + /** + * Returns a matcher which matches the space character. + * + * @return a matcher for a space + */ + public static StrMatcher spaceMatcher() { + return SPACE_MATCHER; + } + + /** + * Matches the same characters as StringTokenizer, + * namely space, tab, newline and formfeed. + * + * @return the split matcher + */ + public static StrMatcher splitMatcher() { + return SPLIT_MATCHER; + } + + /** + * Matches the String trim() whitespace characters. + * + * @return the trim matcher + */ + public static StrMatcher trimMatcher() { + return TRIM_MATCHER; + } + + /** + * Returns a matcher which matches the single quote character. + * + * @return a matcher for a single quote + */ + public static StrMatcher singleQuoteMatcher() { + return SINGLE_QUOTE_MATCHER; + } + + /** + * Returns a matcher which matches the double quote character. + * + * @return a matcher for a double quote + */ + public static StrMatcher doubleQuoteMatcher() { + return DOUBLE_QUOTE_MATCHER; + } + + /** + * Returns a matcher which matches the single or double quote character. + * + * @return a matcher for a single or double quote + */ + public static StrMatcher quoteMatcher() { + return QUOTE_MATCHER; + } + + /** + * Matches no characters. + * + * @return a matcher that matches nothing + */ + public static StrMatcher noneMatcher() { + return NONE_MATCHER; + } + + /** + * Constructor that creates a matcher from a character. + * + * @param ch the character to match, must not be null + * @return a new Matcher for the given char + */ + public static StrMatcher charMatcher(char ch) { + return new CharMatcher(ch); + } + + /** + * Constructor that creates a matcher from a set of characters. + * + * @param chars the characters to match, null or empty matches nothing + * @return a new matcher for the given char[] + */ + public static StrMatcher charSetMatcher(char... chars) { + if (chars == null || chars.length == 0) { + return NONE_MATCHER; + } + if (chars.length == 1) { + return new CharMatcher(chars[0]); + } + return new CharSetMatcher(chars); + } + + /** + * Constructor that creates a matcher from a string representing a set of characters. + * + * @param chars the characters to match, null or empty matches nothing + * @return a new Matcher for the given characters + */ + public static StrMatcher charSetMatcher(String chars) { + if (StringUtils.isEmpty(chars)) { + return NONE_MATCHER; + } + if (chars.length() == 1) { + return new CharMatcher(chars.charAt(0)); + } + return new CharSetMatcher(chars.toCharArray()); + } + + /** + * Constructor that creates a matcher from a string. + * + * @param str the string to match, null or empty matches nothing + * @return a new Matcher for the given String + */ + public static StrMatcher stringMatcher(String str) { + if (StringUtils.isEmpty(str)) { + return NONE_MATCHER; + } + return new StringMatcher(str); + } + + //----------------------------------------------------------------------- + /** + * Constructor. + */ + protected StrMatcher() { + super(); + } + + /** + * Returns the number of matching characters, zero for no match. + *

    + * This method is called to check for a match. + * The parameter pos represents the current position to be + * checked in the string buffer (a character array which must + * not be changed). + * The API guarantees that pos is a valid index for buffer. + *

    + * The character array may be larger than the active area to be matched. + * Only values in the buffer between the specifed indices may be accessed. + *

    + * The matching code may check one character or many. + * It may check characters preceeding pos as well as those + * after, so long as no checks exceed the bounds specified. + *

    + * It must return zero for no match, or a positive number if a match was found. + * The number indicates the number of characters that matched. + * + * @param buffer the text content to match against, do not change + * @param pos the starting position for the match, valid for buffer + * @param bufferStart the first active index in the buffer, valid for buffer + * @param bufferEnd the end index (exclusive) of the active buffer, valid for buffer + * @return the number of matching characters, zero for no match + */ + public abstract int isMatch(char[] buffer, int pos, int bufferStart, int bufferEnd); + + /** + * Returns the number of matching characters, zero for no match. + *

    + * This method is called to check for a match. + * The parameter pos represents the current position to be + * checked in the string buffer (a character array which must + * not be changed). + * The API guarantees that pos is a valid index for buffer. + *

    + * The matching code may check one character or many. + * It may check characters preceeding pos as well as those after. + *

    + * It must return zero for no match, or a positive number if a match was found. + * The number indicates the number of characters that matched. + * + * @param buffer the text content to match against, do not change + * @param pos the starting position for the match, valid for buffer + * @return the number of matching characters, zero for no match + * @since 2.4 + */ + public int isMatch(char[] buffer, int pos) { + return isMatch(buffer, pos, 0, buffer.length); + } + + //----------------------------------------------------------------------- + /** + * Class used to define a set of characters for matching purposes. + */ + static final class CharSetMatcher extends StrMatcher { + /** The set of characters to match. */ + private final char[] chars; + + /** + * Constructor that creates a matcher from a character array. + * + * @param chars the characters to match, must not be null + */ + CharSetMatcher(char chars[]) { + super(); + this.chars = chars.clone(); + Arrays.sort(this.chars); + } + + /** + * Returns whether or not the given character matches. + * + * @param buffer the text content to match against, do not change + * @param pos the starting position for the match, valid for buffer + * @param bufferStart the first active index in the buffer, valid for buffer + * @param bufferEnd the end index of the active buffer, valid for buffer + * @return the number of matching characters, zero for no match + */ + @Override + public int isMatch(char[] buffer, int pos, int bufferStart, int bufferEnd) { + return Arrays.binarySearch(chars, buffer[pos]) >= 0 ? 1 : 0; + } + } + + //----------------------------------------------------------------------- + /** + * Class used to define a character for matching purposes. + */ + static final class CharMatcher extends StrMatcher { + /** The character to match. */ + private final char ch; + + /** + * Constructor that creates a matcher that matches a single character. + * + * @param ch the character to match + */ + CharMatcher(char ch) { + super(); + this.ch = ch; + } + + /** + * Returns whether or not the given character matches. + * + * @param buffer the text content to match against, do not change + * @param pos the starting position for the match, valid for buffer + * @param bufferStart the first active index in the buffer, valid for buffer + * @param bufferEnd the end index of the active buffer, valid for buffer + * @return the number of matching characters, zero for no match + */ + @Override + public int isMatch(char[] buffer, int pos, int bufferStart, int bufferEnd) { + return ch == buffer[pos] ? 1 : 0; + } + } + + //----------------------------------------------------------------------- + /** + * Class used to define a set of characters for matching purposes. + */ + static final class StringMatcher extends StrMatcher { + /** The string to match, as a character array. */ + private final char[] chars; + + /** + * Constructor that creates a matcher from a String. + * + * @param str the string to match, must not be null + */ + StringMatcher(String str) { + super(); + chars = str.toCharArray(); + } + + /** + * Returns whether or not the given text matches the stored string. + * + * @param buffer the text content to match against, do not change + * @param pos the starting position for the match, valid for buffer + * @param bufferStart the first active index in the buffer, valid for buffer + * @param bufferEnd the end index of the active buffer, valid for buffer + * @return the number of matching characters, zero for no match + */ + @Override + public int isMatch(char[] buffer, int pos, int bufferStart, int bufferEnd) { + int len = chars.length; + if (pos + len > bufferEnd) { + return 0; + } + for (int i = 0; i < chars.length; i++, pos++) { + if (chars[i] != buffer[pos]) { + return 0; + } + } + return len; + } + } + + //----------------------------------------------------------------------- + /** + * Class used to match no characters. + */ + static final class NoMatcher extends StrMatcher { + + /** + * Constructs a new instance of NoMatcher. + */ + NoMatcher() { + super(); + } + + /** + * Always returns false. + * + * @param buffer the text content to match against, do not change + * @param pos the starting position for the match, valid for buffer + * @param bufferStart the first active index in the buffer, valid for buffer + * @param bufferEnd the end index of the active buffer, valid for buffer + * @return the number of matching characters, zero for no match + */ + @Override + public int isMatch(char[] buffer, int pos, int bufferStart, int bufferEnd) { + return 0; + } + } + + //----------------------------------------------------------------------- + /** + * Class used to match whitespace as per trim(). + */ + static final class TrimMatcher extends StrMatcher { + + /** + * Constructs a new instance of TrimMatcher. + */ + TrimMatcher() { + super(); + } + + /** + * Returns whether or not the given character matches. + * + * @param buffer the text content to match against, do not change + * @param pos the starting position for the match, valid for buffer + * @param bufferStart the first active index in the buffer, valid for buffer + * @param bufferEnd the end index of the active buffer, valid for buffer + * @return the number of matching characters, zero for no match + */ + @Override + public int isMatch(char[] buffer, int pos, int bufferStart, int bufferEnd) { + return buffer[pos] <= 32 ? 1 : 0; + } + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/text/StrMatcherTest.java b/ApacheCommonsLang/org/apache/commons/lang3/text/StrMatcherTest.java new file mode 100644 index 0000000..9eec2c6 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/text/StrMatcherTest.java @@ -0,0 +1,207 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.lang3.text; + +import junit.framework.TestCase; + +/** + * Unit tests for {@link org.apache.commons.lang3.text.StrMatcher}. + * + * @version $Id: StrMatcherTest.java 892118 2009-12-18 03:39:13Z sebb $ + */ +public class StrMatcherTest extends TestCase { + + private static final char[] BUFFER1 = "0,1\t2 3\n\r\f\u0000'\"".toCharArray(); + + private static final char[] BUFFER2 = "abcdef".toCharArray(); + + /** + * Create a new test case with the specified name. + * + * @param name the name + */ + public StrMatcherTest(String name) { + super(name); + } + + //----------------------------------------------------------------------- + public void testCommaMatcher() { + StrMatcher matcher = StrMatcher.commaMatcher(); + assertSame(matcher, StrMatcher.commaMatcher()); + assertEquals(0, matcher.isMatch(BUFFER1, 0)); + assertEquals(1, matcher.isMatch(BUFFER1, 1)); + assertEquals(0, matcher.isMatch(BUFFER1, 2)); + } + + //----------------------------------------------------------------------- + public void testTabMatcher() { + StrMatcher matcher = StrMatcher.tabMatcher(); + assertSame(matcher, StrMatcher.tabMatcher()); + assertEquals(0, matcher.isMatch(BUFFER1, 2)); + assertEquals(1, matcher.isMatch(BUFFER1, 3)); + assertEquals(0, matcher.isMatch(BUFFER1, 4)); + } + + //----------------------------------------------------------------------- + public void testSpaceMatcher() { + StrMatcher matcher = StrMatcher.spaceMatcher(); + assertSame(matcher, StrMatcher.spaceMatcher()); + assertEquals(0, matcher.isMatch(BUFFER1, 4)); + assertEquals(1, matcher.isMatch(BUFFER1, 5)); + assertEquals(0, matcher.isMatch(BUFFER1, 6)); + } + + //----------------------------------------------------------------------- + public void testSplitMatcher() { + StrMatcher matcher = StrMatcher.splitMatcher(); + assertSame(matcher, StrMatcher.splitMatcher()); + assertEquals(0, matcher.isMatch(BUFFER1, 2)); + assertEquals(1, matcher.isMatch(BUFFER1, 3)); + assertEquals(0, matcher.isMatch(BUFFER1, 4)); + assertEquals(1, matcher.isMatch(BUFFER1, 5)); + assertEquals(0, matcher.isMatch(BUFFER1, 6)); + assertEquals(1, matcher.isMatch(BUFFER1, 7)); + assertEquals(1, matcher.isMatch(BUFFER1, 8)); + assertEquals(1, matcher.isMatch(BUFFER1, 9)); + assertEquals(0, matcher.isMatch(BUFFER1, 10)); + } + + //----------------------------------------------------------------------- + public void testTrimMatcher() { + StrMatcher matcher = StrMatcher.trimMatcher(); + assertSame(matcher, StrMatcher.trimMatcher()); + assertEquals(0, matcher.isMatch(BUFFER1, 2)); + assertEquals(1, matcher.isMatch(BUFFER1, 3)); + assertEquals(0, matcher.isMatch(BUFFER1, 4)); + assertEquals(1, matcher.isMatch(BUFFER1, 5)); + assertEquals(0, matcher.isMatch(BUFFER1, 6)); + assertEquals(1, matcher.isMatch(BUFFER1, 7)); + assertEquals(1, matcher.isMatch(BUFFER1, 8)); + assertEquals(1, matcher.isMatch(BUFFER1, 9)); + assertEquals(1, matcher.isMatch(BUFFER1, 10)); + } + + //----------------------------------------------------------------------- + public void testSingleQuoteMatcher() { + StrMatcher matcher = StrMatcher.singleQuoteMatcher(); + assertSame(matcher, StrMatcher.singleQuoteMatcher()); + assertEquals(0, matcher.isMatch(BUFFER1, 10)); + assertEquals(1, matcher.isMatch(BUFFER1, 11)); + assertEquals(0, matcher.isMatch(BUFFER1, 12)); + } + + //----------------------------------------------------------------------- + public void testDoubleQuoteMatcher() { + StrMatcher matcher = StrMatcher.doubleQuoteMatcher(); + assertSame(matcher, StrMatcher.doubleQuoteMatcher()); + assertEquals(0, matcher.isMatch(BUFFER1, 11)); + assertEquals(1, matcher.isMatch(BUFFER1, 12)); + } + + //----------------------------------------------------------------------- + public void testQuoteMatcher() { + StrMatcher matcher = StrMatcher.quoteMatcher(); + assertSame(matcher, StrMatcher.quoteMatcher()); + assertEquals(0, matcher.isMatch(BUFFER1, 10)); + assertEquals(1, matcher.isMatch(BUFFER1, 11)); + assertEquals(1, matcher.isMatch(BUFFER1, 12)); + } + + //----------------------------------------------------------------------- + public void testNoneMatcher() { + StrMatcher matcher = StrMatcher.noneMatcher(); + assertSame(matcher, StrMatcher.noneMatcher()); + assertEquals(0, matcher.isMatch(BUFFER1, 0)); + assertEquals(0, matcher.isMatch(BUFFER1, 1)); + assertEquals(0, matcher.isMatch(BUFFER1, 2)); + assertEquals(0, matcher.isMatch(BUFFER1, 3)); + assertEquals(0, matcher.isMatch(BUFFER1, 4)); + assertEquals(0, matcher.isMatch(BUFFER1, 5)); + assertEquals(0, matcher.isMatch(BUFFER1, 6)); + assertEquals(0, matcher.isMatch(BUFFER1, 7)); + assertEquals(0, matcher.isMatch(BUFFER1, 8)); + assertEquals(0, matcher.isMatch(BUFFER1, 9)); + assertEquals(0, matcher.isMatch(BUFFER1, 10)); + assertEquals(0, matcher.isMatch(BUFFER1, 11)); + assertEquals(0, matcher.isMatch(BUFFER1, 12)); + } + + //----------------------------------------------------------------------- + public void testCharMatcher_char() { + StrMatcher matcher = StrMatcher.charMatcher('c'); + assertEquals(0, matcher.isMatch(BUFFER2, 0)); + assertEquals(0, matcher.isMatch(BUFFER2, 1)); + assertEquals(1, matcher.isMatch(BUFFER2, 2)); + assertEquals(0, matcher.isMatch(BUFFER2, 3)); + assertEquals(0, matcher.isMatch(BUFFER2, 4)); + assertEquals(0, matcher.isMatch(BUFFER2, 5)); + } + + //----------------------------------------------------------------------- + public void testCharSetMatcher_String() { + StrMatcher matcher = StrMatcher.charSetMatcher("ace"); + assertEquals(1, matcher.isMatch(BUFFER2, 0)); + assertEquals(0, matcher.isMatch(BUFFER2, 1)); + assertEquals(1, matcher.isMatch(BUFFER2, 2)); + assertEquals(0, matcher.isMatch(BUFFER2, 3)); + assertEquals(1, matcher.isMatch(BUFFER2, 4)); + assertEquals(0, matcher.isMatch(BUFFER2, 5)); + assertSame(StrMatcher.noneMatcher(), StrMatcher.charSetMatcher("")); + assertSame(StrMatcher.noneMatcher(), StrMatcher.charSetMatcher((String) null)); + assertTrue(StrMatcher.charSetMatcher("a") instanceof StrMatcher.CharMatcher); + } + + //----------------------------------------------------------------------- + public void testCharSetMatcher_charArray() { + StrMatcher matcher = StrMatcher.charSetMatcher("ace".toCharArray()); + assertEquals(1, matcher.isMatch(BUFFER2, 0)); + assertEquals(0, matcher.isMatch(BUFFER2, 1)); + assertEquals(1, matcher.isMatch(BUFFER2, 2)); + assertEquals(0, matcher.isMatch(BUFFER2, 3)); + assertEquals(1, matcher.isMatch(BUFFER2, 4)); + assertEquals(0, matcher.isMatch(BUFFER2, 5)); + assertSame(StrMatcher.noneMatcher(), StrMatcher.charSetMatcher(new char[0])); + assertSame(StrMatcher.noneMatcher(), StrMatcher.charSetMatcher((char[]) null)); + assertTrue(StrMatcher.charSetMatcher("a".toCharArray()) instanceof StrMatcher.CharMatcher); + } + + //----------------------------------------------------------------------- + public void testStringMatcher_String() { + StrMatcher matcher = StrMatcher.stringMatcher("bc"); + assertEquals(0, matcher.isMatch(BUFFER2, 0)); + assertEquals(2, matcher.isMatch(BUFFER2, 1)); + assertEquals(0, matcher.isMatch(BUFFER2, 2)); + assertEquals(0, matcher.isMatch(BUFFER2, 3)); + assertEquals(0, matcher.isMatch(BUFFER2, 4)); + assertEquals(0, matcher.isMatch(BUFFER2, 5)); + assertSame(StrMatcher.noneMatcher(), StrMatcher.stringMatcher("")); + assertSame(StrMatcher.noneMatcher(), StrMatcher.stringMatcher((String) null)); + } + + //----------------------------------------------------------------------- + public void testMatcherIndices() { + // remember that the API contract is tight for the isMatch() method + // all the onus is on the caller, so invalid inputs are not + // the concern of StrMatcher, and are not bugs + StrMatcher matcher = StrMatcher.stringMatcher("bc"); + assertEquals(2, matcher.isMatch(BUFFER2, 1, 1, BUFFER2.length)); + assertEquals(2, matcher.isMatch(BUFFER2, 1, 0, 3)); + assertEquals(0, matcher.isMatch(BUFFER2, 1, 0, 2)); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/text/StrSubstitutor.java b/ApacheCommonsLang/org/apache/commons/lang3/text/StrSubstitutor.java new file mode 100644 index 0000000..e155417 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/text/StrSubstitutor.java @@ -0,0 +1,926 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.text; + +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +/** + * Substitutes variables within a string by values. + *

    + * This class takes a piece of text and substitutes all the variables within it. + * The default definition of a variable is ${variableName}. + * The prefix and suffix can be changed via constructors and set methods. + *

    + * Variable values are typically resolved from a map, but could also be resolved + * from system properties, or by supplying a custom variable resolver. + *

    + * The simplest example is to use this class to replace Java System properties. For example: + *

    + * StrSubstitutor.replaceSystemProperties(
    + *      "You are running with java.version = ${java.version} and os.name = ${os.name}.");
    + * 
    + *

    + * Typical usage of this class follows the following pattern: First an instance is created + * and initialized with the map that contains the values for the available variables. + * If a prefix and/or suffix for variables should be used other than the default ones, + * the appropriate settings can be performed. After that the replace() + * method can be called passing in the source text for interpolation. In the returned + * text all variable references (as long as their values are known) will be resolved. + * The following example demonstrates this: + *

    + * Map valuesMap = HashMap();
    + * valuesMap.put("animal", "quick brown fox");
    + * valuesMap.put("target", "lazy dog");
    + * String templateString = "The ${animal} jumped over the ${target}.";
    + * StrSubstitutor sub = new StrSubstitutor(valuesMap);
    + * String resolvedString = sub.replace(templateString);
    + * 
    + * yielding: + *
    + *      The quick brown fox jumped over the lazy dog.
    + * 
    + *

    + * In addition to this usage pattern there are some static convenience methods that + * cover the most common use cases. These methods can be used without the need of + * manually creating an instance. However if multiple replace operations are to be + * performed, creating and reusing an instance of this class will be more efficient. + *

    + * Variable replacement works in a recursive way. Thus, if a variable value contains + * a variable then that variable will also be replaced. Cyclic replacements are + * detected and will cause an exception to be thrown. + *

    + * Sometimes the interpolation's result must contain a variable prefix. As an example + * take the following source text: + *

    + *   The variable ${${name}} must be used.
    + * 
    + * Here only the variable's name referred to in the text should be replaced resulting + * in the text (assuming that the value of the name variable is x): + *
    + *   The variable ${x} must be used.
    + * 
    + * To achieve this effect there are two possibilities: Either set a different prefix + * and suffix for variables which do not conflict with the result text you want to + * produce. The other possibility is to use the escape character, by default '$'. + * If this character is placed before a variable reference, this reference is ignored + * and won't be replaced. For example: + *
    + *   The variable $${${name}} must be used.
    + * 
    + *

    + * In some complex scenarios you might even want to perform substitution in the + * names of variables, for instance + *

    + * ${jre-${java.specification.version}}
    + * 
    + * StrSubstitutor supports this recursive substitution in variable + * names, but it has to be enabled explicitly by setting the + * {@link #setEnableSubstitutionInVariables(boolean) enableSubstitutionInVariables} + * property to true. + * + * @version $Id: StrSubstitutor.java 1199894 2011-11-09 17:53:59Z ggregory $ + * @since 2.2 + */ +public class StrSubstitutor { + + /** + * Constant for the default escape character. + */ + public static final char DEFAULT_ESCAPE = '$'; + /** + * Constant for the default variable prefix. + */ + public static final StrMatcher DEFAULT_PREFIX = StrMatcher.stringMatcher("${"); + /** + * Constant for the default variable suffix. + */ + public static final StrMatcher DEFAULT_SUFFIX = StrMatcher.stringMatcher("}"); + + /** + * Stores the escape character. + */ + private char escapeChar; + /** + * Stores the variable prefix. + */ + private StrMatcher prefixMatcher; + /** + * Stores the variable suffix. + */ + private StrMatcher suffixMatcher; + /** + * Variable resolution is delegated to an implementor of VariableResolver. + */ + private StrLookup variableResolver; + /** + * The flag whether substitution in variable names is enabled. + */ + private boolean enableSubstitutionInVariables; + + //----------------------------------------------------------------------- + /** + * Replaces all the occurrences of variables in the given source object with + * their matching values from the map. + * + * @param the type of the values in the map + * @param source the source text containing the variables to substitute, null returns null + * @param valueMap the map with the values, may be null + * @return the result of the replace operation + */ + public static String replace(Object source, Map valueMap) { + return new StrSubstitutor(valueMap).replace(source); + } + + /** + * Replaces all the occurrences of variables in the given source object with + * their matching values from the map. This method allows to specifiy a + * custom variable prefix and suffix + * + * @param the type of the values in the map + * @param source the source text containing the variables to substitute, null returns null + * @param valueMap the map with the values, may be null + * @param prefix the prefix of variables, not null + * @param suffix the suffix of variables, not null + * @return the result of the replace operation + * @throws IllegalArgumentException if the prefix or suffix is null + */ + public static String replace(Object source, Map valueMap, String prefix, String suffix) { + return new StrSubstitutor(valueMap, prefix, suffix).replace(source); + } + + /** + * Replaces all the occurrences of variables in the given source object with their matching + * values from the properties. + * + * @param source the source text containing the variables to substitute, null returns null + * @param valueProperties the properties with values, may be null + * @return the result of the replace operation + */ + public static String replace(Object source, Properties valueProperties) { + if (valueProperties == null) { + return source.toString(); + } + Map valueMap = new HashMap(); + Enumeration propNames = valueProperties.propertyNames(); + while (propNames.hasMoreElements()) { + String propName = (String)propNames.nextElement(); + String propValue = valueProperties.getProperty(propName); + valueMap.put(propName, propValue); + } + return StrSubstitutor.replace(source, valueMap); + } + + /** + * Replaces all the occurrences of variables in the given source object with + * their matching values from the system properties. + * + * @param source the source text containing the variables to substitute, null returns null + * @return the result of the replace operation + */ + public static String replaceSystemProperties(Object source) { + return new StrSubstitutor(StrLookup.systemPropertiesLookup()).replace(source); + } + + //----------------------------------------------------------------------- + /** + * Creates a new instance with defaults for variable prefix and suffix + * and the escaping character. + */ + public StrSubstitutor() { + this((StrLookup) null, DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_ESCAPE); + } + + /** + * Creates a new instance and initializes it. Uses defaults for variable + * prefix and suffix and the escaping character. + * + * @param the type of the values in the map + * @param valueMap the map with the variables' values, may be null + */ + public StrSubstitutor(Map valueMap) { + this(StrLookup.mapLookup(valueMap), DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_ESCAPE); + } + + /** + * Creates a new instance and initializes it. Uses a default escaping character. + * + * @param the type of the values in the map + * @param valueMap the map with the variables' values, may be null + * @param prefix the prefix for variables, not null + * @param suffix the suffix for variables, not null + * @throws IllegalArgumentException if the prefix or suffix is null + */ + public StrSubstitutor(Map valueMap, String prefix, String suffix) { + this(StrLookup.mapLookup(valueMap), prefix, suffix, DEFAULT_ESCAPE); + } + + /** + * Creates a new instance and initializes it. + * + * @param the type of the values in the map + * @param valueMap the map with the variables' values, may be null + * @param prefix the prefix for variables, not null + * @param suffix the suffix for variables, not null + * @param escape the escape character + * @throws IllegalArgumentException if the prefix or suffix is null + */ + public StrSubstitutor(Map valueMap, String prefix, String suffix, char escape) { + this(StrLookup.mapLookup(valueMap), prefix, suffix, escape); + } + + /** + * Creates a new instance and initializes it. + * + * @param variableResolver the variable resolver, may be null + */ + public StrSubstitutor(StrLookup variableResolver) { + this(variableResolver, DEFAULT_PREFIX, DEFAULT_SUFFIX, DEFAULT_ESCAPE); + } + + /** + * Creates a new instance and initializes it. + * + * @param variableResolver the variable resolver, may be null + * @param prefix the prefix for variables, not null + * @param suffix the suffix for variables, not null + * @param escape the escape character + * @throws IllegalArgumentException if the prefix or suffix is null + */ + public StrSubstitutor(StrLookup variableResolver, String prefix, String suffix, char escape) { + this.setVariableResolver(variableResolver); + this.setVariablePrefix(prefix); + this.setVariableSuffix(suffix); + this.setEscapeChar(escape); + } + + /** + * Creates a new instance and initializes it. + * + * @param variableResolver the variable resolver, may be null + * @param prefixMatcher the prefix for variables, not null + * @param suffixMatcher the suffix for variables, not null + * @param escape the escape character + * @throws IllegalArgumentException if the prefix or suffix is null + */ + public StrSubstitutor( + StrLookup variableResolver, StrMatcher prefixMatcher, StrMatcher suffixMatcher, char escape) { + this.setVariableResolver(variableResolver); + this.setVariablePrefixMatcher(prefixMatcher); + this.setVariableSuffixMatcher(suffixMatcher); + this.setEscapeChar(escape); + } + + //----------------------------------------------------------------------- + /** + * Replaces all the occurrences of variables with their matching values + * from the resolver using the given source string as a template. + * + * @param source the string to replace in, null returns null + * @return the result of the replace operation + */ + public String replace(String source) { + if (source == null) { + return null; + } + StrBuilder buf = new StrBuilder(source); + if (substitute(buf, 0, source.length()) == false) { + return source; + } + return buf.toString(); + } + + /** + * Replaces all the occurrences of variables with their matching values + * from the resolver using the given source string as a template. + *

    + * Only the specified portion of the string will be processed. + * The rest of the string is not processed, and is not returned. + * + * @param source the string to replace in, null returns null + * @param offset the start offset within the array, must be valid + * @param length the length within the array to be processed, must be valid + * @return the result of the replace operation + */ + public String replace(String source, int offset, int length) { + if (source == null) { + return null; + } + StrBuilder buf = new StrBuilder(length).append(source, offset, length); + if (substitute(buf, 0, length) == false) { + return source.substring(offset, offset + length); + } + return buf.toString(); + } + + //----------------------------------------------------------------------- + /** + * Replaces all the occurrences of variables with their matching values + * from the resolver using the given source array as a template. + * The array is not altered by this method. + * + * @param source the character array to replace in, not altered, null returns null + * @return the result of the replace operation + */ + public String replace(char[] source) { + if (source == null) { + return null; + } + StrBuilder buf = new StrBuilder(source.length).append(source); + substitute(buf, 0, source.length); + return buf.toString(); + } + + /** + * Replaces all the occurrences of variables with their matching values + * from the resolver using the given source array as a template. + * The array is not altered by this method. + *

    + * Only the specified portion of the array will be processed. + * The rest of the array is not processed, and is not returned. + * + * @param source the character array to replace in, not altered, null returns null + * @param offset the start offset within the array, must be valid + * @param length the length within the array to be processed, must be valid + * @return the result of the replace operation + */ + public String replace(char[] source, int offset, int length) { + if (source == null) { + return null; + } + StrBuilder buf = new StrBuilder(length).append(source, offset, length); + substitute(buf, 0, length); + return buf.toString(); + } + + //----------------------------------------------------------------------- + /** + * Replaces all the occurrences of variables with their matching values + * from the resolver using the given source buffer as a template. + * The buffer is not altered by this method. + * + * @param source the buffer to use as a template, not changed, null returns null + * @return the result of the replace operation + */ + public String replace(StringBuffer source) { + if (source == null) { + return null; + } + StrBuilder buf = new StrBuilder(source.length()).append(source); + substitute(buf, 0, buf.length()); + return buf.toString(); + } + + /** + * Replaces all the occurrences of variables with their matching values + * from the resolver using the given source buffer as a template. + * The buffer is not altered by this method. + *

    + * Only the specified portion of the buffer will be processed. + * The rest of the buffer is not processed, and is not returned. + * + * @param source the buffer to use as a template, not changed, null returns null + * @param offset the start offset within the array, must be valid + * @param length the length within the array to be processed, must be valid + * @return the result of the replace operation + */ + public String replace(StringBuffer source, int offset, int length) { + if (source == null) { + return null; + } + StrBuilder buf = new StrBuilder(length).append(source, offset, length); + substitute(buf, 0, length); + return buf.toString(); + } + + //----------------------------------------------------------------------- + /** + * Replaces all the occurrences of variables with their matching values + * from the resolver using the given source builder as a template. + * The builder is not altered by this method. + * + * @param source the builder to use as a template, not changed, null returns null + * @return the result of the replace operation + */ + public String replace(StrBuilder source) { + if (source == null) { + return null; + } + StrBuilder buf = new StrBuilder(source.length()).append(source); + substitute(buf, 0, buf.length()); + return buf.toString(); + } + + /** + * Replaces all the occurrences of variables with their matching values + * from the resolver using the given source builder as a template. + * The builder is not altered by this method. + *

    + * Only the specified portion of the builder will be processed. + * The rest of the builder is not processed, and is not returned. + * + * @param source the builder to use as a template, not changed, null returns null + * @param offset the start offset within the array, must be valid + * @param length the length within the array to be processed, must be valid + * @return the result of the replace operation + */ + public String replace(StrBuilder source, int offset, int length) { + if (source == null) { + return null; + } + StrBuilder buf = new StrBuilder(length).append(source, offset, length); + substitute(buf, 0, length); + return buf.toString(); + } + + //----------------------------------------------------------------------- + /** + * Replaces all the occurrences of variables in the given source object with + * their matching values from the resolver. The input source object is + * converted to a string using toString and is not altered. + * + * @param source the source to replace in, null returns null + * @return the result of the replace operation + */ + public String replace(Object source) { + if (source == null) { + return null; + } + StrBuilder buf = new StrBuilder().append(source); + substitute(buf, 0, buf.length()); + return buf.toString(); + } + + //----------------------------------------------------------------------- + /** + * Replaces all the occurrences of variables within the given source buffer + * with their matching values from the resolver. + * The buffer is updated with the result. + * + * @param source the buffer to replace in, updated, null returns zero + * @return true if altered + */ + public boolean replaceIn(StringBuffer source) { + if (source == null) { + return false; + } + return replaceIn(source, 0, source.length()); + } + + /** + * Replaces all the occurrences of variables within the given source buffer + * with their matching values from the resolver. + * The buffer is updated with the result. + *

    + * Only the specified portion of the buffer will be processed. + * The rest of the buffer is not processed, but it is not deleted. + * + * @param source the buffer to replace in, updated, null returns zero + * @param offset the start offset within the array, must be valid + * @param length the length within the buffer to be processed, must be valid + * @return true if altered + */ + public boolean replaceIn(StringBuffer source, int offset, int length) { + if (source == null) { + return false; + } + StrBuilder buf = new StrBuilder(length).append(source, offset, length); + if (substitute(buf, 0, length) == false) { + return false; + } + source.replace(offset, offset + length, buf.toString()); + return true; + } + + //----------------------------------------------------------------------- + /** + * Replaces all the occurrences of variables within the given source + * builder with their matching values from the resolver. + * + * @param source the builder to replace in, updated, null returns zero + * @return true if altered + */ + public boolean replaceIn(StrBuilder source) { + if (source == null) { + return false; + } + return substitute(source, 0, source.length()); + } + + /** + * Replaces all the occurrences of variables within the given source + * builder with their matching values from the resolver. + *

    + * Only the specified portion of the builder will be processed. + * The rest of the builder is not processed, but it is not deleted. + * + * @param source the builder to replace in, null returns zero + * @param offset the start offset within the array, must be valid + * @param length the length within the builder to be processed, must be valid + * @return true if altered + */ + public boolean replaceIn(StrBuilder source, int offset, int length) { + if (source == null) { + return false; + } + return substitute(source, offset, length); + } + + //----------------------------------------------------------------------- + /** + * Internal method that substitutes the variables. + *

    + * Most users of this class do not need to call this method. This method will + * be called automatically by another (public) method. + *

    + * Writers of subclasses can override this method if they need access to + * the substitution process at the start or end. + * + * @param buf the string builder to substitute into, not null + * @param offset the start offset within the builder, must be valid + * @param length the length within the builder to be processed, must be valid + * @return true if altered + */ + protected boolean substitute(StrBuilder buf, int offset, int length) { + return substitute(buf, offset, length, null) > 0; + } + + /** + * Recursive handler for multiple levels of interpolation. This is the main + * interpolation method, which resolves the values of all variable references + * contained in the passed in text. + * + * @param buf the string builder to substitute into, not null + * @param offset the start offset within the builder, must be valid + * @param length the length within the builder to be processed, must be valid + * @param priorVariables the stack keeping track of the replaced variables, may be null + * @return the length change that occurs, unless priorVariables is null when the int + * represents a boolean flag as to whether any change occurred. + */ + private int substitute(StrBuilder buf, int offset, int length, List priorVariables) { + StrMatcher prefixMatcher = getVariablePrefixMatcher(); + StrMatcher suffixMatcher = getVariableSuffixMatcher(); + char escape = getEscapeChar(); + + boolean top = priorVariables == null; + boolean altered = false; + int lengthChange = 0; + char[] chars = buf.buffer; + int bufEnd = offset + length; + int pos = offset; + while (pos < bufEnd) { + int startMatchLen = prefixMatcher.isMatch(chars, pos, offset, + bufEnd); + if (startMatchLen == 0) { + pos++; + } else { + // found variable start marker + if (pos > offset && chars[pos - 1] == escape) { + // escaped + buf.deleteCharAt(pos - 1); + chars = buf.buffer; // in case buffer was altered + lengthChange--; + altered = true; + bufEnd--; + } else { + // find suffix + int startPos = pos; + pos += startMatchLen; + int endMatchLen = 0; + int nestedVarCount = 0; + while (pos < bufEnd) { + if (isEnableSubstitutionInVariables() + && (endMatchLen = prefixMatcher.isMatch(chars, + pos, offset, bufEnd)) != 0) { + // found a nested variable start + nestedVarCount++; + pos += endMatchLen; + continue; + } + + endMatchLen = suffixMatcher.isMatch(chars, pos, offset, + bufEnd); + if (endMatchLen == 0) { + pos++; + } else { + // found variable end marker + if (nestedVarCount == 0) { + String varName = new String(chars, startPos + + startMatchLen, pos - startPos + - startMatchLen); + if (isEnableSubstitutionInVariables()) { + StrBuilder bufName = new StrBuilder(varName); + substitute(bufName, 0, bufName.length()); + varName = bufName.toString(); + } + pos += endMatchLen; + int endPos = pos; + + // on the first call initialize priorVariables + if (priorVariables == null) { + priorVariables = new ArrayList(); + priorVariables.add(new String(chars, + offset, length)); + } + + // handle cyclic substitution + checkCyclicSubstitution(varName, priorVariables); + priorVariables.add(varName); + + // resolve the variable + String varValue = resolveVariable(varName, buf, + startPos, endPos); + if (varValue != null) { + // recursive replace + int varLen = varValue.length(); + buf.replace(startPos, endPos, varValue); + altered = true; + int change = substitute(buf, startPos, + varLen, priorVariables); + change = change + + varLen - (endPos - startPos); + pos += change; + bufEnd += change; + lengthChange += change; + chars = buf.buffer; // in case buffer was + // altered + } + + // remove variable from the cyclic stack + priorVariables + .remove(priorVariables.size() - 1); + break; + } else { + nestedVarCount--; + pos += endMatchLen; + } + } + } + } + } + } + if (top) { + return altered ? 1 : 0; + } + return lengthChange; + } + + /** + * Checks if the specified variable is already in the stack (list) of variables. + * + * @param varName the variable name to check + * @param priorVariables the list of prior variables + */ + private void checkCyclicSubstitution(String varName, List priorVariables) { + if (priorVariables.contains(varName) == false) { + return; + } + StrBuilder buf = new StrBuilder(256); + buf.append("Infinite loop in property interpolation of "); + buf.append(priorVariables.remove(0)); + buf.append(": "); + buf.appendWithSeparators(priorVariables, "->"); + throw new IllegalStateException(buf.toString()); + } + + /** + * Internal method that resolves the value of a variable. + *

    + * Most users of this class do not need to call this method. This method is + * called automatically by the substitution process. + *

    + * Writers of subclasses can override this method if they need to alter + * how each substitution occurs. The method is passed the variable's name + * and must return the corresponding value. This implementation uses the + * {@link #getVariableResolver()} with the variable's name as the key. + * + * @param variableName the name of the variable, not null + * @param buf the buffer where the substitution is occurring, not null + * @param startPos the start position of the variable including the prefix, valid + * @param endPos the end position of the variable including the suffix, valid + * @return the variable's value or null if the variable is unknown + */ + protected String resolveVariable(String variableName, StrBuilder buf, int startPos, int endPos) { + StrLookup resolver = getVariableResolver(); + if (resolver == null) { + return null; + } + return resolver.lookup(variableName); + } + + // Escape + //----------------------------------------------------------------------- + /** + * Returns the escape character. + * + * @return the character used for escaping variable references + */ + public char getEscapeChar() { + return this.escapeChar; + } + + /** + * Sets the escape character. + * If this character is placed before a variable reference in the source + * text, this variable will be ignored. + * + * @param escapeCharacter the escape character (0 for disabling escaping) + */ + public void setEscapeChar(char escapeCharacter) { + this.escapeChar = escapeCharacter; + } + + // Prefix + //----------------------------------------------------------------------- + /** + * Gets the variable prefix matcher currently in use. + *

    + * The variable prefix is the characer or characters that identify the + * start of a variable. This prefix is expressed in terms of a matcher + * allowing advanced prefix matches. + * + * @return the prefix matcher in use + */ + public StrMatcher getVariablePrefixMatcher() { + return prefixMatcher; + } + + /** + * Sets the variable prefix matcher currently in use. + *

    + * The variable prefix is the characer or characters that identify the + * start of a variable. This prefix is expressed in terms of a matcher + * allowing advanced prefix matches. + * + * @param prefixMatcher the prefix matcher to use, null ignored + * @return this, to enable chaining + * @throws IllegalArgumentException if the prefix matcher is null + */ + public StrSubstitutor setVariablePrefixMatcher(StrMatcher prefixMatcher) { + if (prefixMatcher == null) { + throw new IllegalArgumentException("Variable prefix matcher must not be null!"); + } + this.prefixMatcher = prefixMatcher; + return this; + } + + /** + * Sets the variable prefix to use. + *

    + * The variable prefix is the character or characters that identify the + * start of a variable. This method allows a single character prefix to + * be easily set. + * + * @param prefix the prefix character to use + * @return this, to enable chaining + */ + public StrSubstitutor setVariablePrefix(char prefix) { + return setVariablePrefixMatcher(StrMatcher.charMatcher(prefix)); + } + + /** + * Sets the variable prefix to use. + *

    + * The variable prefix is the characer or characters that identify the + * start of a variable. This method allows a string prefix to be easily set. + * + * @param prefix the prefix for variables, not null + * @return this, to enable chaining + * @throws IllegalArgumentException if the prefix is null + */ + public StrSubstitutor setVariablePrefix(String prefix) { + if (prefix == null) { + throw new IllegalArgumentException("Variable prefix must not be null!"); + } + return setVariablePrefixMatcher(StrMatcher.stringMatcher(prefix)); + } + + // Suffix + //----------------------------------------------------------------------- + /** + * Gets the variable suffix matcher currently in use. + *

    + * The variable suffix is the characer or characters that identify the + * end of a variable. This suffix is expressed in terms of a matcher + * allowing advanced suffix matches. + * + * @return the suffix matcher in use + */ + public StrMatcher getVariableSuffixMatcher() { + return suffixMatcher; + } + + /** + * Sets the variable suffix matcher currently in use. + *

    + * The variable suffix is the characer or characters that identify the + * end of a variable. This suffix is expressed in terms of a matcher + * allowing advanced suffix matches. + * + * @param suffixMatcher the suffix matcher to use, null ignored + * @return this, to enable chaining + * @throws IllegalArgumentException if the suffix matcher is null + */ + public StrSubstitutor setVariableSuffixMatcher(StrMatcher suffixMatcher) { + if (suffixMatcher == null) { + throw new IllegalArgumentException("Variable suffix matcher must not be null!"); + } + this.suffixMatcher = suffixMatcher; + return this; + } + + /** + * Sets the variable suffix to use. + *

    + * The variable suffix is the characer or characters that identify the + * end of a variable. This method allows a single character suffix to + * be easily set. + * + * @param suffix the suffix character to use + * @return this, to enable chaining + */ + public StrSubstitutor setVariableSuffix(char suffix) { + return setVariableSuffixMatcher(StrMatcher.charMatcher(suffix)); + } + + /** + * Sets the variable suffix to use. + *

    + * The variable suffix is the character or characters that identify the + * end of a variable. This method allows a string suffix to be easily set. + * + * @param suffix the suffix for variables, not null + * @return this, to enable chaining + * @throws IllegalArgumentException if the suffix is null + */ + public StrSubstitutor setVariableSuffix(String suffix) { + if (suffix == null) { + throw new IllegalArgumentException("Variable suffix must not be null!"); + } + return setVariableSuffixMatcher(StrMatcher.stringMatcher(suffix)); + } + + // Resolver + //----------------------------------------------------------------------- + /** + * Gets the VariableResolver that is used to lookup variables. + * + * @return the VariableResolver + */ + public StrLookup getVariableResolver() { + return this.variableResolver; + } + + /** + * Sets the VariableResolver that is used to lookup variables. + * + * @param variableResolver the VariableResolver + */ + public void setVariableResolver(StrLookup variableResolver) { + this.variableResolver = variableResolver; + } + + // Substitution support in variable names + //----------------------------------------------------------------------- + /** + * Returns a flag whether substitution is done in variable names. + * + * @return the substitution in variable names flag + * @since 3.0 + */ + public boolean isEnableSubstitutionInVariables() { + return enableSubstitutionInVariables; + } + + /** + * Sets a flag whether substitution is done in variable names. If set to + * true, the names of variables can contain other variables which are + * processed first before the original variable is evaluated, e.g. + * ${jre-${java.version}}. The default value is false. + * + * @param enableSubstitutionInVariables the new value of the flag + * @since 3.0 + */ + public void setEnableSubstitutionInVariables( + boolean enableSubstitutionInVariables) { + this.enableSubstitutionInVariables = enableSubstitutionInVariables; + } +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/text/StrSubstitutorTest.java b/ApacheCommonsLang/org/apache/commons/lang3/text/StrSubstitutorTest.java new file mode 100644 index 0000000..fb5e69a --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/text/StrSubstitutorTest.java @@ -0,0 +1,570 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.lang3.text; + +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +import junit.framework.TestCase; + +import org.apache.commons.lang3.mutable.MutableObject; + +/** + * Test class for StrSubstitutor. + * + * @version $Id: StrSubstitutorTest.java 1243121 2012-02-11 18:57:19Z mbenson $ + */ +public class StrSubstitutorTest extends TestCase { + + private Map values; + + @Override + protected void setUp() throws Exception { + super.setUp(); + values = new HashMap(); + values.put("animal", "quick brown fox"); + values.put("target", "lazy dog"); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + values = null; + } + + //----------------------------------------------------------------------- + /** + * Tests simple key replace. + */ + public void testReplaceSimple() { + doTestReplace("The quick brown fox jumps over the lazy dog.", "The ${animal} jumps over the ${target}.", true); + } + + /** + * Tests simple key replace. + */ + public void testReplaceSolo() { + doTestReplace("quick brown fox", "${animal}", false); + } + + /** + * Tests replace with no variables. + */ + public void testReplaceNoVariables() { + doTestNoReplace("The balloon arrived."); + } + + /** + * Tests replace with null. + */ + public void testReplaceNull() { + doTestNoReplace(null); + } + + /** + * Tests replace with null. + */ + public void testReplaceEmpty() { + doTestNoReplace(""); + } + + /** + * Tests key replace changing map after initialization (not recommended). + */ + public void testReplaceChangedMap() { + StrSubstitutor sub = new StrSubstitutor(values); + values.put("target", "moon"); + assertEquals("The quick brown fox jumps over the moon.", sub.replace("The ${animal} jumps over the ${target}.")); + } + + /** + * Tests unknown key replace. + */ + public void testReplaceUnknownKey() { + doTestReplace("The ${person} jumps over the lazy dog.", "The ${person} jumps over the ${target}.", true); + } + + /** + * Tests adjacent keys. + */ + public void testReplaceAdjacentAtStart() { + values.put("code", "GBP"); + values.put("amount", "12.50"); + StrSubstitutor sub = new StrSubstitutor(values); + assertEquals("GBP12.50 charged", sub.replace("${code}${amount} charged")); + } + + /** + * Tests adjacent keys. + */ + public void testReplaceAdjacentAtEnd() { + values.put("code", "GBP"); + values.put("amount", "12.50"); + StrSubstitutor sub = new StrSubstitutor(values); + assertEquals("Amount is GBP12.50", sub.replace("Amount is ${code}${amount}")); + } + + /** + * Tests simple recursive replace. + */ + public void testReplaceRecursive() { + values.put("animal", "${critter}"); + values.put("target", "${pet}"); + values.put("pet", "${petCharacteristic} dog"); + values.put("petCharacteristic", "lazy"); + values.put("critter", "${critterSpeed} ${critterColor} ${critterType}"); + values.put("critterSpeed", "quick"); + values.put("critterColor", "brown"); + values.put("critterType", "fox"); + doTestReplace("The quick brown fox jumps over the lazy dog.", "The ${animal} jumps over the ${target}.", true); + } + + /** + * Tests escaping. + */ + public void testReplaceEscaping() { + doTestReplace("The ${animal} jumps over the lazy dog.", "The $${animal} jumps over the ${target}.", true); + } + + /** + * Tests escaping. + */ + public void testReplaceSoloEscaping() { + doTestReplace("${animal}", "$${animal}", false); + } + + /** + * Tests complex escaping. + */ + public void testReplaceComplexEscaping() { + doTestReplace("The ${quick brown fox} jumps over the lazy dog.", "The $${${animal}} jumps over the ${target}.", true); + } + + /** + * Tests when no prefix or suffix. + */ + public void testReplaceNoPrefixNoSuffix() { + doTestReplace("The animal jumps over the lazy dog.", "The animal jumps over the ${target}.", true); + } + + /** + * Tests when no incomplete prefix. + */ + public void testReplaceIncompletePrefix() { + doTestReplace("The {animal} jumps over the lazy dog.", "The {animal} jumps over the ${target}.", true); + } + + /** + * Tests when prefix but no suffix. + */ + public void testReplacePrefixNoSuffix() { + doTestReplace("The ${animal jumps over the ${target} lazy dog.", "The ${animal jumps over the ${target} ${target}.", true); + } + + /** + * Tests when suffix but no prefix. + */ + public void testReplaceNoPrefixSuffix() { + doTestReplace("The animal} jumps over the lazy dog.", "The animal} jumps over the ${target}.", true); + } + + /** + * Tests when no variable name. + */ + public void testReplaceEmptyKeys() { + doTestReplace("The ${} jumps over the lazy dog.", "The ${} jumps over the ${target}.", true); + } + + /** + * Tests replace creates output same as input. + */ + public void testReplaceToIdentical() { + values.put("animal", "$${${thing}}"); + values.put("thing", "animal"); + doTestReplace("The ${animal} jumps.", "The ${animal} jumps.", true); + } + + /** + * Tests a cyclic replace operation. + * The cycle should be detected and cause an exception to be thrown. + */ + public void testCyclicReplacement() { + Map map = new HashMap(); + map.put("animal", "${critter}"); + map.put("target", "${pet}"); + map.put("pet", "${petCharacteristic} dog"); + map.put("petCharacteristic", "lazy"); + map.put("critter", "${critterSpeed} ${critterColor} ${critterType}"); + map.put("critterSpeed", "quick"); + map.put("critterColor", "brown"); + map.put("critterType", "${animal}"); + StrSubstitutor sub = new StrSubstitutor(map); + try { + sub.replace("The ${animal} jumps over the ${target}."); + fail("Cyclic replacement was not detected!"); + } catch (IllegalStateException ex) { + // expected + } + } + + /** + * Tests interpolation with weird boundary patterns. + */ + public void testReplaceWeirdPattens() { + doTestNoReplace(""); + doTestNoReplace("${}"); + doTestNoReplace("${ }"); + doTestNoReplace("${\t}"); + doTestNoReplace("${\n}"); + doTestNoReplace("${\b}"); + doTestNoReplace("${"); + doTestNoReplace("$}"); + doTestNoReplace("}"); + doTestNoReplace("${}$"); + doTestNoReplace("${${"); + doTestNoReplace("${${}}"); + doTestNoReplace("${$${}}"); + doTestNoReplace("${$$${}}"); + doTestNoReplace("${$$${$}}"); + doTestNoReplace("${${}}"); + doTestNoReplace("${${ }}"); + } + + /** + * Tests simple key replace. + */ + public void testReplacePartialString_noReplace() { + StrSubstitutor sub = new StrSubstitutor(); + assertEquals("${animal} jumps", sub.replace("The ${animal} jumps over the ${target}.", 4, 15)); + } + + /** + * Tests whether a variable can be replaced in a variable name. + */ + public void testReplaceInVariable() { + values.put("animal.1", "fox"); + values.put("animal.2", "mouse"); + values.put("species", "2"); + StrSubstitutor sub = new StrSubstitutor(values); + sub.setEnableSubstitutionInVariables(true); + assertEquals( + "Wrong result (1)", + "The mouse jumps over the lazy dog.", + sub.replace("The ${animal.${species}} jumps over the ${target}.")); + values.put("species", "1"); + assertEquals( + "Wrong result (2)", + "The fox jumps over the lazy dog.", + sub.replace("The ${animal.${species}} jumps over the ${target}.")); + } + + /** + * Tests whether substitution in variable names is disabled per default. + */ + public void testReplaceInVariableDisabled() { + values.put("animal.1", "fox"); + values.put("animal.2", "mouse"); + values.put("species", "2"); + StrSubstitutor sub = new StrSubstitutor(values); + assertEquals( + "Wrong result", + "The ${animal.${species}} jumps over the lazy dog.", + sub.replace("The ${animal.${species}} jumps over the ${target}.")); + } + + /** + * Tests complex and recursive substitution in variable names. + */ + public void testReplaceInVariableRecursive() { + values.put("animal.2", "brown fox"); + values.put("animal.1", "white mouse"); + values.put("color", "white"); + values.put("species.white", "1"); + values.put("species.brown", "2"); + StrSubstitutor sub = new StrSubstitutor(values); + sub.setEnableSubstitutionInVariables(true); + assertEquals( + "Wrong result", + "The white mouse jumps over the lazy dog.", + sub.replace("The ${animal.${species.${color}}} jumps over the ${target}.")); + } + + //----------------------------------------------------------------------- + /** + * Tests protected. + */ + public void testResolveVariable() { + final StrBuilder builder = new StrBuilder("Hi ${name}!"); + Map map = new HashMap(); + map.put("name", "commons"); + StrSubstitutor sub = new StrSubstitutor(map) { + @Override + protected String resolveVariable(String variableName, StrBuilder buf, int startPos, int endPos) { + assertEquals("name", variableName); + assertSame(builder, buf); + assertEquals(3, startPos); + assertEquals(10, endPos); + return "jakarta"; + } + }; + sub.replaceIn(builder); + assertEquals("Hi jakarta!", builder.toString()); + } + + //----------------------------------------------------------------------- + /** + * Tests constructor. + */ + public void testConstructorNoArgs() { + StrSubstitutor sub = new StrSubstitutor(); + assertEquals("Hi ${name}", sub.replace("Hi ${name}")); + } + + /** + * Tests constructor. + */ + public void testConstructorMapPrefixSuffix() { + Map map = new HashMap(); + map.put("name", "commons"); + StrSubstitutor sub = new StrSubstitutor(map, "<", ">"); + assertEquals("Hi < commons", sub.replace("Hi $< ")); + } + + /** + * Tests constructor. + */ + public void testConstructorMapFull() { + Map map = new HashMap(); + map.put("name", "commons"); + StrSubstitutor sub = new StrSubstitutor(map, "<", ">", '!'); + assertEquals("Hi < commons", sub.replace("Hi !< ")); + } + + //----------------------------------------------------------------------- + /** + * Tests get set. + */ + public void testGetSetEscape() { + StrSubstitutor sub = new StrSubstitutor(); + assertEquals('$', sub.getEscapeChar()); + sub.setEscapeChar('<'); + assertEquals('<', sub.getEscapeChar()); + } + + /** + * Tests get set. + */ + public void testGetSetPrefix() { + StrSubstitutor sub = new StrSubstitutor(); + assertEquals(true, sub.getVariablePrefixMatcher() instanceof StrMatcher.StringMatcher); + sub.setVariablePrefix('<'); + assertEquals(true, sub.getVariablePrefixMatcher() instanceof StrMatcher.CharMatcher); + + sub.setVariablePrefix("<<"); + assertEquals(true, sub.getVariablePrefixMatcher() instanceof StrMatcher.StringMatcher); + try { + sub.setVariablePrefix((String) null); + fail(); + } catch (IllegalArgumentException ex) { + // expected + } + assertEquals(true, sub.getVariablePrefixMatcher() instanceof StrMatcher.StringMatcher); + + StrMatcher matcher = StrMatcher.commaMatcher(); + sub.setVariablePrefixMatcher(matcher); + assertSame(matcher, sub.getVariablePrefixMatcher()); + try { + sub.setVariablePrefixMatcher((StrMatcher) null); + fail(); + } catch (IllegalArgumentException ex) { + // expected + } + assertSame(matcher, sub.getVariablePrefixMatcher()); + } + + /** + * Tests get set. + */ + public void testGetSetSuffix() { + StrSubstitutor sub = new StrSubstitutor(); + assertEquals(true, sub.getVariableSuffixMatcher() instanceof StrMatcher.StringMatcher); + sub.setVariableSuffix('<'); + assertEquals(true, sub.getVariableSuffixMatcher() instanceof StrMatcher.CharMatcher); + + sub.setVariableSuffix("<<"); + assertEquals(true, sub.getVariableSuffixMatcher() instanceof StrMatcher.StringMatcher); + try { + sub.setVariableSuffix((String) null); + fail(); + } catch (IllegalArgumentException ex) { + // expected + } + assertEquals(true, sub.getVariableSuffixMatcher() instanceof StrMatcher.StringMatcher); + + StrMatcher matcher = StrMatcher.commaMatcher(); + sub.setVariableSuffixMatcher(matcher); + assertSame(matcher, sub.getVariableSuffixMatcher()); + try { + sub.setVariableSuffixMatcher((StrMatcher) null); + fail(); + } catch (IllegalArgumentException ex) { + // expected + } + assertSame(matcher, sub.getVariableSuffixMatcher()); + } + + //----------------------------------------------------------------------- + /** + * Tests static. + */ + public void testStaticReplace() { + Map map = new HashMap(); + map.put("name", "commons"); + assertEquals("Hi commons!", StrSubstitutor.replace("Hi ${name}!", map)); + } + + /** + * Tests static. + */ + public void testStaticReplacePrefixSuffix() { + Map map = new HashMap(); + map.put("name", "commons"); + assertEquals("Hi commons!", StrSubstitutor.replace("Hi !", map, "<", ">")); + } + + /** + * Tests interpolation with system properties. + */ + public void testStaticReplaceSystemProperties() { + StrBuilder buf = new StrBuilder(); + buf.append("Hi ").append(System.getProperty("user.name")); + buf.append(", you are working with "); + buf.append(System.getProperty("os.name")); + buf.append(", your home directory is "); + buf.append(System.getProperty("user.home")).append('.'); + assertEquals(buf.toString(), StrSubstitutor.replaceSystemProperties("Hi ${user.name}, you are " + + "working with ${os.name}, your home " + + "directory is ${user.home}.")); + } + + /** + * Test the replace of a properties object + */ + public void testSubstituteDefaultProperties(){ + String org = "${doesnotwork}"; + System.setProperty("doesnotwork", "It works!"); + + // create a new Properties object with the System.getProperties as default + Properties props = new Properties(System.getProperties()); + + assertEquals("It works!", StrSubstitutor.replace(org, props)); + } + + public void testSamePrefixAndSuffix() { + Map map = new HashMap(); + map.put("greeting", "Hello"); + map.put(" there ", "XXX"); + map.put("name", "commons"); + assertEquals("Hi commons!", StrSubstitutor.replace("Hi @name@!", map, "@", "@")); + assertEquals("Hello there commons!", StrSubstitutor.replace("@greeting@ there @name@!", map, "@", "@")); + } + + //----------------------------------------------------------------------- + private void doTestReplace(String expectedResult, String replaceTemplate, boolean substring) { + String expectedShortResult = expectedResult.substring(1, expectedResult.length() - 1); + StrSubstitutor sub = new StrSubstitutor(values); + + // replace using String + assertEquals(expectedResult, sub.replace(replaceTemplate)); + if (substring) { + assertEquals(expectedShortResult, sub.replace(replaceTemplate, 1, replaceTemplate.length() - 2)); + } + + // replace using char[] + char[] chars = replaceTemplate.toCharArray(); + assertEquals(expectedResult, sub.replace(chars)); + if (substring) { + assertEquals(expectedShortResult, sub.replace(chars, 1, chars.length - 2)); + } + + // replace using StringBuffer + StringBuffer buf = new StringBuffer(replaceTemplate); + assertEquals(expectedResult, sub.replace(buf)); + if (substring) { + assertEquals(expectedShortResult, sub.replace(buf, 1, buf.length() - 2)); + } + + // replace using StrBuilder + StrBuilder bld = new StrBuilder(replaceTemplate); + assertEquals(expectedResult, sub.replace(bld)); + if (substring) { + assertEquals(expectedShortResult, sub.replace(bld, 1, bld.length() - 2)); + } + + // replace using object + MutableObject obj = new MutableObject(replaceTemplate); // toString returns template + assertEquals(expectedResult, sub.replace(obj)); + + // replace in StringBuffer + buf = new StringBuffer(replaceTemplate); + assertEquals(true, sub.replaceIn(buf)); + assertEquals(expectedResult, buf.toString()); + if (substring) { + buf = new StringBuffer(replaceTemplate); + assertEquals(true, sub.replaceIn(buf, 1, buf.length() - 2)); + assertEquals(expectedResult, buf.toString()); // expect full result as remainder is untouched + } + + // replace in StrBuilder + bld = new StrBuilder(replaceTemplate); + assertEquals(true, sub.replaceIn(bld)); + assertEquals(expectedResult, bld.toString()); + if (substring) { + bld = new StrBuilder(replaceTemplate); + assertEquals(true, sub.replaceIn(bld, 1, bld.length() - 2)); + assertEquals(expectedResult, bld.toString()); // expect full result as remainder is untouched + } + } + + private void doTestNoReplace(String replaceTemplate) { + StrSubstitutor sub = new StrSubstitutor(values); + + if (replaceTemplate == null) { + assertEquals(null, sub.replace((String) null)); + assertEquals(null, sub.replace((String) null, 0, 100)); + assertEquals(null, sub.replace((char[]) null)); + assertEquals(null, sub.replace((char[]) null, 0, 100)); + assertEquals(null, sub.replace((StringBuffer) null)); + assertEquals(null, sub.replace((StringBuffer) null, 0, 100)); + assertEquals(null, sub.replace((StrBuilder) null)); + assertEquals(null, sub.replace((StrBuilder) null, 0, 100)); + assertEquals(null, sub.replace((Object) null)); + assertEquals(false, sub.replaceIn((StringBuffer) null)); + assertEquals(false, sub.replaceIn((StringBuffer) null, 0, 100)); + assertEquals(false, sub.replaceIn((StrBuilder) null)); + assertEquals(false, sub.replaceIn((StrBuilder) null, 0, 100)); + } else { + assertEquals(replaceTemplate, sub.replace(replaceTemplate)); + StrBuilder bld = new StrBuilder(replaceTemplate); + assertEquals(false, sub.replaceIn(bld)); + assertEquals(replaceTemplate, bld.toString()); + } + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/text/StrTokenizer.java b/ApacheCommonsLang/org/apache/commons/lang3/text/StrTokenizer.java new file mode 100644 index 0000000..197efe9 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/text/StrTokenizer.java @@ -0,0 +1,1114 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.text; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.ListIterator; +import java.util.NoSuchElementException; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.StringUtils; + +/** + * Tokenizes a string based based on delimiters (separators) + * and supporting quoting and ignored character concepts. + *

    + * This class can split a String into many smaller strings. It aims + * to do a similar job to {@link java.util.StringTokenizer StringTokenizer}, + * however it offers much more control and flexibility including implementing + * the ListIterator interface. By default, it is set up + * like StringTokenizer. + *

    + * The input String is split into a number of tokens. + * Each token is separated from the next String by a delimiter. + * One or more delimiter characters must be specified. + *

    + * Each token may be surrounded by quotes. + * The quote matcher specifies the quote character(s). + * A quote may be escaped within a quoted section by duplicating itself. + *

    + * Between each token and the delimiter are potentially characters that need trimming. + * The trimmer matcher specifies these characters. + * One usage might be to trim whitespace characters. + *

    + * At any point outside the quotes there might potentially be invalid characters. + * The ignored matcher specifies these characters to be removed. + * One usage might be to remove new line characters. + *

    + * Empty tokens may be removed or returned as null. + *

    + * "a,b,c"         - Three tokens "a","b","c"   (comma delimiter)
    + * " a, b , c "    - Three tokens "a","b","c"   (default CSV processing trims whitespace)
    + * "a, ", b ,", c" - Three tokens "a, " , " b ", ", c" (quoted text untouched)
    + * 
    + *

    + * + * This tokenizer has the following properties and options: + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
    PropertyTypeDefault
    delimCharSetMatcher{ \t\n\r\f}
    quoteNoneMatcher{}
    ignoreNoneMatcher{}
    emptyTokenAsNullbooleanfalse
    ignoreEmptyTokensbooleantrue
    + * + * @since 2.2 + * @version $Id: StrTokenizer.java 1309977 2012-04-05 17:53:39Z ggregory $ + */ +public class StrTokenizer implements ListIterator, Cloneable { + + private static final StrTokenizer CSV_TOKENIZER_PROTOTYPE; + private static final StrTokenizer TSV_TOKENIZER_PROTOTYPE; + static { + CSV_TOKENIZER_PROTOTYPE = new StrTokenizer(); + CSV_TOKENIZER_PROTOTYPE.setDelimiterMatcher(StrMatcher.commaMatcher()); + CSV_TOKENIZER_PROTOTYPE.setQuoteMatcher(StrMatcher.doubleQuoteMatcher()); + CSV_TOKENIZER_PROTOTYPE.setIgnoredMatcher(StrMatcher.noneMatcher()); + CSV_TOKENIZER_PROTOTYPE.setTrimmerMatcher(StrMatcher.trimMatcher()); + CSV_TOKENIZER_PROTOTYPE.setEmptyTokenAsNull(false); + CSV_TOKENIZER_PROTOTYPE.setIgnoreEmptyTokens(false); + + TSV_TOKENIZER_PROTOTYPE = new StrTokenizer(); + TSV_TOKENIZER_PROTOTYPE.setDelimiterMatcher(StrMatcher.tabMatcher()); + TSV_TOKENIZER_PROTOTYPE.setQuoteMatcher(StrMatcher.doubleQuoteMatcher()); + TSV_TOKENIZER_PROTOTYPE.setIgnoredMatcher(StrMatcher.noneMatcher()); + TSV_TOKENIZER_PROTOTYPE.setTrimmerMatcher(StrMatcher.trimMatcher()); + TSV_TOKENIZER_PROTOTYPE.setEmptyTokenAsNull(false); + TSV_TOKENIZER_PROTOTYPE.setIgnoreEmptyTokens(false); + } + + /** The text to work on. */ + private char chars[]; + /** The parsed tokens */ + private String tokens[]; + /** The current iteration position */ + private int tokenPos; + + /** The delimiter matcher */ + private StrMatcher delimMatcher = StrMatcher.splitMatcher(); + /** The quote matcher */ + private StrMatcher quoteMatcher = StrMatcher.noneMatcher(); + /** The ignored matcher */ + private StrMatcher ignoredMatcher = StrMatcher.noneMatcher(); + /** The trimmer matcher */ + private StrMatcher trimmerMatcher = StrMatcher.noneMatcher(); + + /** Whether to return empty tokens as null */ + private boolean emptyAsNull = false; + /** Whether to ignore empty tokens */ + private boolean ignoreEmptyTokens = true; + + //----------------------------------------------------------------------- + + /** + * Returns a clone of CSV_TOKENIZER_PROTOTYPE. + * + * @return a clone of CSV_TOKENIZER_PROTOTYPE. + */ + private static StrTokenizer getCSVClone() { + return (StrTokenizer) CSV_TOKENIZER_PROTOTYPE.clone(); + } + + /** + * Gets a new tokenizer instance which parses Comma Separated Value strings + * initializing it with the given input. The default for CSV processing + * will be trim whitespace from both ends (which can be overridden with + * the setTrimmer method). + *

    + * You must call a "reset" method to set the string which you want to parse. + * @return a new tokenizer instance which parses Comma Separated Value strings + */ + public static StrTokenizer getCSVInstance() { + return getCSVClone(); + } + + /** + * Gets a new tokenizer instance which parses Comma Separated Value strings + * initializing it with the given input. The default for CSV processing + * will be trim whitespace from both ends (which can be overridden with + * the setTrimmer method). + * + * @param input the text to parse + * @return a new tokenizer instance which parses Comma Separated Value strings + */ + public static StrTokenizer getCSVInstance(String input) { + StrTokenizer tok = getCSVClone(); + tok.reset(input); + return tok; + } + + /** + * Gets a new tokenizer instance which parses Comma Separated Value strings + * initializing it with the given input. The default for CSV processing + * will be trim whitespace from both ends (which can be overridden with + * the setTrimmer method). + * + * @param input the text to parse + * @return a new tokenizer instance which parses Comma Separated Value strings + */ + public static StrTokenizer getCSVInstance(char[] input) { + StrTokenizer tok = getCSVClone(); + tok.reset(input); + return tok; + } + + /** + * Returns a clone of TSV_TOKENIZER_PROTOTYPE. + * + * @return a clone of TSV_TOKENIZER_PROTOTYPE. + */ + private static StrTokenizer getTSVClone() { + return (StrTokenizer) TSV_TOKENIZER_PROTOTYPE.clone(); + } + + + /** + * Gets a new tokenizer instance which parses Tab Separated Value strings. + * The default for CSV processing will be trim whitespace from both ends + * (which can be overridden with the setTrimmer method). + *

    + * You must call a "reset" method to set the string which you want to parse. + * @return a new tokenizer instance which parses Tab Separated Value strings. + */ + public static StrTokenizer getTSVInstance() { + return getTSVClone(); + } + + /** + * Gets a new tokenizer instance which parses Tab Separated Value strings. + * The default for CSV processing will be trim whitespace from both ends + * (which can be overridden with the setTrimmer method). + * @param input the string to parse + * @return a new tokenizer instance which parses Tab Separated Value strings. + */ + public static StrTokenizer getTSVInstance(String input) { + StrTokenizer tok = getTSVClone(); + tok.reset(input); + return tok; + } + + /** + * Gets a new tokenizer instance which parses Tab Separated Value strings. + * The default for CSV processing will be trim whitespace from both ends + * (which can be overridden with the setTrimmer method). + * @param input the string to parse + * @return a new tokenizer instance which parses Tab Separated Value strings. + */ + public static StrTokenizer getTSVInstance(char[] input) { + StrTokenizer tok = getTSVClone(); + tok.reset(input); + return tok; + } + + //----------------------------------------------------------------------- + /** + * Constructs a tokenizer splitting on space, tab, newline and formfeed + * as per StringTokenizer, but with no text to tokenize. + *

    + * This constructor is normally used with {@link #reset(String)}. + */ + public StrTokenizer() { + super(); + this.chars = null; + } + + /** + * Constructs a tokenizer splitting on space, tab, newline and formfeed + * as per StringTokenizer. + * + * @param input the string which is to be parsed + */ + public StrTokenizer(String input) { + super(); + if (input != null) { + chars = input.toCharArray(); + } else { + chars = null; + } + } + + /** + * Constructs a tokenizer splitting on the specified delimiter character. + * + * @param input the string which is to be parsed + * @param delim the field delimiter character + */ + public StrTokenizer(String input, char delim) { + this(input); + setDelimiterChar(delim); + } + + /** + * Constructs a tokenizer splitting on the specified delimiter string. + * + * @param input the string which is to be parsed + * @param delim the field delimiter string + */ + public StrTokenizer(String input, String delim) { + this(input); + setDelimiterString(delim); + } + + /** + * Constructs a tokenizer splitting using the specified delimiter matcher. + * + * @param input the string which is to be parsed + * @param delim the field delimiter matcher + */ + public StrTokenizer(String input, StrMatcher delim) { + this(input); + setDelimiterMatcher(delim); + } + + /** + * Constructs a tokenizer splitting on the specified delimiter character + * and handling quotes using the specified quote character. + * + * @param input the string which is to be parsed + * @param delim the field delimiter character + * @param quote the field quoted string character + */ + public StrTokenizer(String input, char delim, char quote) { + this(input, delim); + setQuoteChar(quote); + } + + /** + * Constructs a tokenizer splitting using the specified delimiter matcher + * and handling quotes using the specified quote matcher. + * + * @param input the string which is to be parsed + * @param delim the field delimiter matcher + * @param quote the field quoted string matcher + */ + public StrTokenizer(String input, StrMatcher delim, StrMatcher quote) { + this(input, delim); + setQuoteMatcher(quote); + } + + /** + * Constructs a tokenizer splitting on space, tab, newline and formfeed + * as per StringTokenizer. + * + * @param input the string which is to be parsed, not cloned + */ + public StrTokenizer(char[] input) { + super(); + this.chars = ArrayUtils.clone(input); + } + + /** + * Constructs a tokenizer splitting on the specified character. + * + * @param input the string which is to be parsed, not cloned + * @param delim the field delimiter character + */ + public StrTokenizer(char[] input, char delim) { + this(input); + setDelimiterChar(delim); + } + + /** + * Constructs a tokenizer splitting on the specified string. + * + * @param input the string which is to be parsed, not cloned + * @param delim the field delimiter string + */ + public StrTokenizer(char[] input, String delim) { + this(input); + setDelimiterString(delim); + } + + /** + * Constructs a tokenizer splitting using the specified delimiter matcher. + * + * @param input the string which is to be parsed, not cloned + * @param delim the field delimiter matcher + */ + public StrTokenizer(char[] input, StrMatcher delim) { + this(input); + setDelimiterMatcher(delim); + } + + /** + * Constructs a tokenizer splitting on the specified delimiter character + * and handling quotes using the specified quote character. + * + * @param input the string which is to be parsed, not cloned + * @param delim the field delimiter character + * @param quote the field quoted string character + */ + public StrTokenizer(char[] input, char delim, char quote) { + this(input, delim); + setQuoteChar(quote); + } + + /** + * Constructs a tokenizer splitting using the specified delimiter matcher + * and handling quotes using the specified quote matcher. + * + * @param input the string which is to be parsed, not cloned + * @param delim the field delimiter character + * @param quote the field quoted string character + */ + public StrTokenizer(char[] input, StrMatcher delim, StrMatcher quote) { + this(input, delim); + setQuoteMatcher(quote); + } + + // API + //----------------------------------------------------------------------- + /** + * Gets the number of tokens found in the String. + * + * @return the number of matched tokens + */ + public int size() { + checkTokenized(); + return tokens.length; + } + + /** + * Gets the next token from the String. + * Equivalent to {@link #next()} except it returns null rather than + * throwing {@link NoSuchElementException} when no tokens remain. + * + * @return the next sequential token, or null when no more tokens are found + */ + public String nextToken() { + if (hasNext()) { + return tokens[tokenPos++]; + } + return null; + } + + /** + * Gets the previous token from the String. + * + * @return the previous sequential token, or null when no more tokens are found + */ + public String previousToken() { + if (hasPrevious()) { + return tokens[--tokenPos]; + } + return null; + } + + /** + * Gets a copy of the full token list as an independent modifiable array. + * + * @return the tokens as a String array + */ + public String[] getTokenArray() { + checkTokenized(); + return tokens.clone(); + } + + /** + * Gets a copy of the full token list as an independent modifiable list. + * + * @return the tokens as a String array + */ + public List getTokenList() { + checkTokenized(); + List list = new ArrayList(tokens.length); + for (String element : tokens) { + list.add(element); + } + return list; + } + + /** + * Resets this tokenizer, forgetting all parsing and iteration already completed. + *

    + * This method allows the same tokenizer to be reused for the same String. + * + * @return this, to enable chaining + */ + public StrTokenizer reset() { + tokenPos = 0; + tokens = null; + return this; + } + + /** + * Reset this tokenizer, giving it a new input string to parse. + * In this manner you can re-use a tokenizer with the same settings + * on multiple input lines. + * + * @param input the new string to tokenize, null sets no text to parse + * @return this, to enable chaining + */ + public StrTokenizer reset(String input) { + reset(); + if (input != null) { + this.chars = input.toCharArray(); + } else { + this.chars = null; + } + return this; + } + + /** + * Reset this tokenizer, giving it a new input string to parse. + * In this manner you can re-use a tokenizer with the same settings + * on multiple input lines. + * + * @param input the new character array to tokenize, not cloned, null sets no text to parse + * @return this, to enable chaining + */ + public StrTokenizer reset(char[] input) { + reset(); + this.chars = ArrayUtils.clone(input); + return this; + } + + // ListIterator + //----------------------------------------------------------------------- + /** + * Checks whether there are any more tokens. + * + * @return true if there are more tokens + */ + @Override + public boolean hasNext() { + checkTokenized(); + return tokenPos < tokens.length; + } + + /** + * Gets the next token. + * + * @return the next String token + * @throws NoSuchElementException if there are no more elements + */ + @Override + public String next() { + if (hasNext()) { + return tokens[tokenPos++]; + } + throw new NoSuchElementException(); + } + + /** + * Gets the index of the next token to return. + * + * @return the next token index + */ + @Override + public int nextIndex() { + return tokenPos; + } + + /** + * Checks whether there are any previous tokens that can be iterated to. + * + * @return true if there are previous tokens + */ + @Override + public boolean hasPrevious() { + checkTokenized(); + return tokenPos > 0; + } + + /** + * Gets the token previous to the last returned token. + * + * @return the previous token + */ + @Override + public String previous() { + if (hasPrevious()) { + return tokens[--tokenPos]; + } + throw new NoSuchElementException(); + } + + /** + * Gets the index of the previous token. + * + * @return the previous token index + */ + @Override + public int previousIndex() { + return tokenPos - 1; + } + + /** + * Unsupported ListIterator operation. + * + * @throws UnsupportedOperationException always + */ + @Override + public void remove() { + throw new UnsupportedOperationException("remove() is unsupported"); + } + + /** + * Unsupported ListIterator operation. + * @param obj this parameter ignored. + * @throws UnsupportedOperationException always + */ + @Override + public void set(String obj) { + throw new UnsupportedOperationException("set() is unsupported"); + } + + /** + * Unsupported ListIterator operation. + * @param obj this parameter ignored. + * @throws UnsupportedOperationException always + */ + @Override + public void add(String obj) { + throw new UnsupportedOperationException("add() is unsupported"); + } + + // Implementation + //----------------------------------------------------------------------- + /** + * Checks if tokenization has been done, and if not then do it. + */ + private void checkTokenized() { + if (tokens == null) { + if (chars == null) { + // still call tokenize as subclass may do some work + List split = tokenize(null, 0, 0); + tokens = split.toArray(new String[split.size()]); + } else { + List split = tokenize(chars, 0, chars.length); + tokens = split.toArray(new String[split.size()]); + } + } + } + + /** + * Internal method to performs the tokenization. + *

    + * Most users of this class do not need to call this method. This method + * will be called automatically by other (public) methods when required. + *

    + * This method exists to allow subclasses to add code before or after the + * tokenization. For example, a subclass could alter the character array, + * offset or count to be parsed, or call the tokenizer multiple times on + * multiple strings. It is also be possible to filter the results. + *

    + * StrTokenizer will always pass a zero offset and a count + * equal to the length of the array to this method, however a subclass + * may pass other values, or even an entirely different array. + * + * @param chars the character array being tokenized, may be null + * @param offset the start position within the character array, must be valid + * @param count the number of characters to tokenize, must be valid + * @return the modifiable list of String tokens, unmodifiable if null array or zero count + */ + protected List tokenize(char[] chars, int offset, int count) { + if (chars == null || count == 0) { + return Collections.emptyList(); + } + StrBuilder buf = new StrBuilder(); + List tokens = new ArrayList(); + int pos = offset; + + // loop around the entire buffer + while (pos >= 0 && pos < count) { + // find next token + pos = readNextToken(chars, pos, count, buf, tokens); + + // handle case where end of string is a delimiter + if (pos >= count) { + addToken(tokens, ""); + } + } + return tokens; + } + + /** + * Adds a token to a list, paying attention to the parameters we've set. + * + * @param list the list to add to + * @param tok the token to add + */ + private void addToken(List list, String tok) { + if (StringUtils.isEmpty(tok)) { + if (isIgnoreEmptyTokens()) { + return; + } + if (isEmptyTokenAsNull()) { + tok = null; + } + } + list.add(tok); + } + + /** + * Reads character by character through the String to get the next token. + * + * @param chars the character array being tokenized + * @param start the first character of field + * @param len the length of the character array being tokenized + * @param workArea a temporary work area + * @param tokens the list of parsed tokens + * @return the starting position of the next field (the character + * immediately after the delimiter), or -1 if end of string found + */ + private int readNextToken(char[] chars, int start, int len, StrBuilder workArea, List tokens) { + // skip all leading whitespace, unless it is the + // field delimiter or the quote character + while (start < len) { + int removeLen = Math.max( + getIgnoredMatcher().isMatch(chars, start, start, len), + getTrimmerMatcher().isMatch(chars, start, start, len)); + if (removeLen == 0 || + getDelimiterMatcher().isMatch(chars, start, start, len) > 0 || + getQuoteMatcher().isMatch(chars, start, start, len) > 0) { + break; + } + start += removeLen; + } + + // handle reaching end + if (start >= len) { + addToken(tokens, ""); + return -1; + } + + // handle empty token + int delimLen = getDelimiterMatcher().isMatch(chars, start, start, len); + if (delimLen > 0) { + addToken(tokens, ""); + return start + delimLen; + } + + // handle found token + int quoteLen = getQuoteMatcher().isMatch(chars, start, start, len); + if (quoteLen > 0) { + return readWithQuotes(chars, start + quoteLen, len, workArea, tokens, start, quoteLen); + } + return readWithQuotes(chars, start, len, workArea, tokens, 0, 0); + } + + /** + * Reads a possibly quoted string token. + * + * @param chars the character array being tokenized + * @param start the first character of field + * @param len the length of the character array being tokenized + * @param workArea a temporary work area + * @param tokens the list of parsed tokens + * @param quoteStart the start position of the matched quote, 0 if no quoting + * @param quoteLen the length of the matched quote, 0 if no quoting + * @return the starting position of the next field (the character + * immediately after the delimiter, or if end of string found, + * then the length of string + */ + private int readWithQuotes(char[] chars, int start, int len, StrBuilder workArea, + List tokens, int quoteStart, int quoteLen) { + // Loop until we've found the end of the quoted + // string or the end of the input + workArea.clear(); + int pos = start; + boolean quoting = quoteLen > 0; + int trimStart = 0; + + while (pos < len) { + // quoting mode can occur several times throughout a string + // we must switch between quoting and non-quoting until we + // encounter a non-quoted delimiter, or end of string + if (quoting) { + // In quoting mode + + // If we've found a quote character, see if it's + // followed by a second quote. If so, then we need + // to actually put the quote character into the token + // rather than end the token. + if (isQuote(chars, pos, len, quoteStart, quoteLen)) { + if (isQuote(chars, pos + quoteLen, len, quoteStart, quoteLen)) { + // matched pair of quotes, thus an escaped quote + workArea.append(chars, pos, quoteLen); + pos += quoteLen * 2; + trimStart = workArea.size(); + continue; + } + + // end of quoting + quoting = false; + pos += quoteLen; + continue; + } + + // copy regular character from inside quotes + workArea.append(chars[pos++]); + trimStart = workArea.size(); + + } else { + // Not in quoting mode + + // check for delimiter, and thus end of token + int delimLen = getDelimiterMatcher().isMatch(chars, pos, start, len); + if (delimLen > 0) { + // return condition when end of token found + addToken(tokens, workArea.substring(0, trimStart)); + return pos + delimLen; + } + + // check for quote, and thus back into quoting mode + if (quoteLen > 0 && isQuote(chars, pos, len, quoteStart, quoteLen)) { + quoting = true; + pos += quoteLen; + continue; + } + + // check for ignored (outside quotes), and ignore + int ignoredLen = getIgnoredMatcher().isMatch(chars, pos, start, len); + if (ignoredLen > 0) { + pos += ignoredLen; + continue; + } + + // check for trimmed character + // don't yet know if its at the end, so copy to workArea + // use trimStart to keep track of trim at the end + int trimmedLen = getTrimmerMatcher().isMatch(chars, pos, start, len); + if (trimmedLen > 0) { + workArea.append(chars, pos, trimmedLen); + pos += trimmedLen; + continue; + } + + // copy regular character from outside quotes + workArea.append(chars[pos++]); + trimStart = workArea.size(); + } + } + + // return condition when end of string found + addToken(tokens, workArea.substring(0, trimStart)); + return -1; + } + + /** + * Checks if the characters at the index specified match the quote + * already matched in readNextToken(). + * + * @param chars the character array being tokenized + * @param pos the position to check for a quote + * @param len the length of the character array being tokenized + * @param quoteStart the start position of the matched quote, 0 if no quoting + * @param quoteLen the length of the matched quote, 0 if no quoting + * @return true if a quote is matched + */ + private boolean isQuote(char[] chars, int pos, int len, int quoteStart, int quoteLen) { + for (int i = 0; i < quoteLen; i++) { + if (pos + i >= len || chars[pos + i] != chars[quoteStart + i]) { + return false; + } + } + return true; + } + + // Delimiter + //----------------------------------------------------------------------- + /** + * Gets the field delimiter matcher. + * + * @return the delimiter matcher in use + */ + public StrMatcher getDelimiterMatcher() { + return this.delimMatcher; + } + + /** + * Sets the field delimiter matcher. + *

    + * The delimitier is used to separate one token from another. + * + * @param delim the delimiter matcher to use + * @return this, to enable chaining + */ + public StrTokenizer setDelimiterMatcher(StrMatcher delim) { + if (delim == null) { + this.delimMatcher = StrMatcher.noneMatcher(); + } else { + this.delimMatcher = delim; + } + return this; + } + + /** + * Sets the field delimiter character. + * + * @param delim the delimiter character to use + * @return this, to enable chaining + */ + public StrTokenizer setDelimiterChar(char delim) { + return setDelimiterMatcher(StrMatcher.charMatcher(delim)); + } + + /** + * Sets the field delimiter string. + * + * @param delim the delimiter string to use + * @return this, to enable chaining + */ + public StrTokenizer setDelimiterString(String delim) { + return setDelimiterMatcher(StrMatcher.stringMatcher(delim)); + } + + // Quote + //----------------------------------------------------------------------- + /** + * Gets the quote matcher currently in use. + *

    + * The quote character is used to wrap data between the tokens. + * This enables delimiters to be entered as data. + * The default value is '"' (double quote). + * + * @return the quote matcher in use + */ + public StrMatcher getQuoteMatcher() { + return quoteMatcher; + } + + /** + * Set the quote matcher to use. + *

    + * The quote character is used to wrap data between the tokens. + * This enables delimiters to be entered as data. + * + * @param quote the quote matcher to use, null ignored + * @return this, to enable chaining + */ + public StrTokenizer setQuoteMatcher(StrMatcher quote) { + if (quote != null) { + this.quoteMatcher = quote; + } + return this; + } + + /** + * Sets the quote character to use. + *

    + * The quote character is used to wrap data between the tokens. + * This enables delimiters to be entered as data. + * + * @param quote the quote character to use + * @return this, to enable chaining + */ + public StrTokenizer setQuoteChar(char quote) { + return setQuoteMatcher(StrMatcher.charMatcher(quote)); + } + + // Ignored + //----------------------------------------------------------------------- + /** + * Gets the ignored character matcher. + *

    + * These characters are ignored when parsing the String, unless they are + * within a quoted region. + * The default value is not to ignore anything. + * + * @return the ignored matcher in use + */ + public StrMatcher getIgnoredMatcher() { + return ignoredMatcher; + } + + /** + * Set the matcher for characters to ignore. + *

    + * These characters are ignored when parsing the String, unless they are + * within a quoted region. + * + * @param ignored the ignored matcher to use, null ignored + * @return this, to enable chaining + */ + public StrTokenizer setIgnoredMatcher(StrMatcher ignored) { + if (ignored != null) { + this.ignoredMatcher = ignored; + } + return this; + } + + /** + * Set the character to ignore. + *

    + * This character is ignored when parsing the String, unless it is + * within a quoted region. + * + * @param ignored the ignored character to use + * @return this, to enable chaining + */ + public StrTokenizer setIgnoredChar(char ignored) { + return setIgnoredMatcher(StrMatcher.charMatcher(ignored)); + } + + // Trimmer + //----------------------------------------------------------------------- + /** + * Gets the trimmer character matcher. + *

    + * These characters are trimmed off on each side of the delimiter + * until the token or quote is found. + * The default value is not to trim anything. + * + * @return the trimmer matcher in use + */ + public StrMatcher getTrimmerMatcher() { + return trimmerMatcher; + } + + /** + * Sets the matcher for characters to trim. + *

    + * These characters are trimmed off on each side of the delimiter + * until the token or quote is found. + * + * @param trimmer the trimmer matcher to use, null ignored + * @return this, to enable chaining + */ + public StrTokenizer setTrimmerMatcher(StrMatcher trimmer) { + if (trimmer != null) { + this.trimmerMatcher = trimmer; + } + return this; + } + + //----------------------------------------------------------------------- + /** + * Gets whether the tokenizer currently returns empty tokens as null. + * The default for this property is false. + * + * @return true if empty tokens are returned as null + */ + public boolean isEmptyTokenAsNull() { + return this.emptyAsNull; + } + + /** + * Sets whether the tokenizer should return empty tokens as null. + * The default for this property is false. + * + * @param emptyAsNull whether empty tokens are returned as null + * @return this, to enable chaining + */ + public StrTokenizer setEmptyTokenAsNull(boolean emptyAsNull) { + this.emptyAsNull = emptyAsNull; + return this; + } + + //----------------------------------------------------------------------- + /** + * Gets whether the tokenizer currently ignores empty tokens. + * The default for this property is true. + * + * @return true if empty tokens are not returned + */ + public boolean isIgnoreEmptyTokens() { + return ignoreEmptyTokens; + } + + /** + * Sets whether the tokenizer should ignore and not return empty tokens. + * The default for this property is true. + * + * @param ignoreEmptyTokens whether empty tokens are not returned + * @return this, to enable chaining + */ + public StrTokenizer setIgnoreEmptyTokens(boolean ignoreEmptyTokens) { + this.ignoreEmptyTokens = ignoreEmptyTokens; + return this; + } + + //----------------------------------------------------------------------- + /** + * Gets the String content that the tokenizer is parsing. + * + * @return the string content being parsed + */ + public String getContent() { + if (chars == null) { + return null; + } + return new String(chars); + } + + //----------------------------------------------------------------------- + /** + * Creates a new instance of this Tokenizer. The new instance is reset so + * that it will be at the start of the token list. + * If a {@link CloneNotSupportedException} is caught, return null. + * + * @return a new instance of this Tokenizer which has been reset. + */ + @Override + public Object clone() { + try { + return cloneReset(); + } catch (CloneNotSupportedException ex) { + return null; + } + } + + /** + * Creates a new instance of this Tokenizer. The new instance is reset so that + * it will be at the start of the token list. + * + * @return a new instance of this Tokenizer which has been reset. + * @throws CloneNotSupportedException if there is a problem cloning + */ + Object cloneReset() throws CloneNotSupportedException { + // this method exists to enable 100% test coverage + StrTokenizer cloned = (StrTokenizer) super.clone(); + if (cloned.chars != null) { + cloned.chars = cloned.chars.clone(); + } + cloned.reset(); + return cloned; + } + + //----------------------------------------------------------------------- + /** + * Gets the String content that the tokenizer is parsing. + * + * @return the string content being parsed + */ + @Override + public String toString() { + if (tokens == null) { + return "StrTokenizer[not tokenized yet]"; + } + return "StrTokenizer" + getTokenList(); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/text/StrTokenizerTest.java b/ApacheCommonsLang/org/apache/commons/lang3/text/StrTokenizerTest.java new file mode 100644 index 0000000..7149417 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/text/StrTokenizerTest.java @@ -0,0 +1,833 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.lang3.text; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.NoSuchElementException; + +import junit.framework.TestCase; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.ObjectUtils; + +/** + * Unit test for Tokenizer. + * + */ +public class StrTokenizerTest extends TestCase { + + private static final String CSV_SIMPLE_FIXTURE = "A,b,c"; + + private static final String TSV_SIMPLE_FIXTURE = "A\tb\tc"; + + /** + * JUnit constructor. + * + * @param name + */ + public StrTokenizerTest(String name) { + super(name); + } + + private void checkClone(StrTokenizer tokenizer) { + assertFalse(StrTokenizer.getCSVInstance() == tokenizer); + assertFalse(StrTokenizer.getTSVInstance() == tokenizer); + } + + // ----------------------------------------------------------------------- + public void test1() { + + String input = "a;b;c;\"d;\"\"e\";f; ; ; "; + StrTokenizer tok = new StrTokenizer(input); + tok.setDelimiterChar(';'); + tok.setQuoteChar('"'); + tok.setIgnoredMatcher(StrMatcher.trimMatcher()); + tok.setIgnoreEmptyTokens(false); + String tokens[] = tok.getTokenArray(); + + String expected[] = new String[]{"a", "b", "c", "d;\"e", "f", "", "", "",}; + + assertEquals(ArrayUtils.toString(tokens), expected.length, tokens.length); + for (int i = 0; i < expected.length; i++) { + assertTrue("token[" + i + "] was '" + tokens[i] + "' but was expected to be '" + expected[i] + "'", + ObjectUtils.equals(expected[i], tokens[i])); + } + + } + + public void test2() { + + String input = "a;b;c ;\"d;\"\"e\";f; ; ;"; + StrTokenizer tok = new StrTokenizer(input); + tok.setDelimiterChar(';'); + tok.setQuoteChar('"'); + tok.setIgnoredMatcher(StrMatcher.noneMatcher()); + tok.setIgnoreEmptyTokens(false); + String tokens[] = tok.getTokenArray(); + + String expected[] = new String[]{"a", "b", "c ", "d;\"e", "f", " ", " ", "",}; + + assertEquals(ArrayUtils.toString(tokens), expected.length, tokens.length); + for (int i = 0; i < expected.length; i++) { + assertTrue("token[" + i + "] was '" + tokens[i] + "' but was expected to be '" + expected[i] + "'", + ObjectUtils.equals(expected[i], tokens[i])); + } + + } + + public void test3() { + + String input = "a;b; c;\"d;\"\"e\";f; ; ;"; + StrTokenizer tok = new StrTokenizer(input); + tok.setDelimiterChar(';'); + tok.setQuoteChar('"'); + tok.setIgnoredMatcher(StrMatcher.noneMatcher()); + tok.setIgnoreEmptyTokens(false); + String tokens[] = tok.getTokenArray(); + + String expected[] = new String[]{"a", "b", " c", "d;\"e", "f", " ", " ", "",}; + + assertEquals(ArrayUtils.toString(tokens), expected.length, tokens.length); + for (int i = 0; i < expected.length; i++) { + assertTrue("token[" + i + "] was '" + tokens[i] + "' but was expected to be '" + expected[i] + "'", + ObjectUtils.equals(expected[i], tokens[i])); + } + + } + + public void test4() { + + String input = "a;b; c;\"d;\"\"e\";f; ; ;"; + StrTokenizer tok = new StrTokenizer(input); + tok.setDelimiterChar(';'); + tok.setQuoteChar('"'); + tok.setIgnoredMatcher(StrMatcher.trimMatcher()); + tok.setIgnoreEmptyTokens(true); + String tokens[] = tok.getTokenArray(); + + String expected[] = new String[]{"a", "b", "c", "d;\"e", "f",}; + + assertEquals(ArrayUtils.toString(tokens), expected.length, tokens.length); + for (int i = 0; i < expected.length; i++) { + assertTrue("token[" + i + "] was '" + tokens[i] + "' but was expected to be '" + expected[i] + "'", + ObjectUtils.equals(expected[i], tokens[i])); + } + + } + + public void test5() { + + String input = "a;b; c;\"d;\"\"e\";f; ; ;"; + StrTokenizer tok = new StrTokenizer(input); + tok.setDelimiterChar(';'); + tok.setQuoteChar('"'); + tok.setIgnoredMatcher(StrMatcher.trimMatcher()); + tok.setIgnoreEmptyTokens(false); + tok.setEmptyTokenAsNull(true); + String tokens[] = tok.getTokenArray(); + + String expected[] = new String[]{"a", "b", "c", "d;\"e", "f", null, null, null,}; + + assertEquals(ArrayUtils.toString(tokens), expected.length, tokens.length); + for (int i = 0; i < expected.length; i++) { + assertTrue("token[" + i + "] was '" + tokens[i] + "' but was expected to be '" + expected[i] + "'", + ObjectUtils.equals(expected[i], tokens[i])); + } + + } + + public void test6() { + + String input = "a;b; c;\"d;\"\"e\";f; ; ;"; + StrTokenizer tok = new StrTokenizer(input); + tok.setDelimiterChar(';'); + tok.setQuoteChar('"'); + tok.setIgnoredMatcher(StrMatcher.trimMatcher()); + tok.setIgnoreEmptyTokens(false); + // tok.setTreatingEmptyAsNull(true); + String tokens[] = tok.getTokenArray(); + + String expected[] = new String[]{"a", "b", " c", "d;\"e", "f", null, null, null,}; + + int nextCount = 0; + while (tok.hasNext()) { + tok.next(); + nextCount++; + } + + int prevCount = 0; + while (tok.hasPrevious()) { + tok.previous(); + prevCount++; + } + + assertEquals(ArrayUtils.toString(tokens), expected.length, tokens.length); + + assertTrue("could not cycle through entire token list" + " using the 'hasNext' and 'next' methods", + nextCount == expected.length); + + assertTrue("could not cycle through entire token list" + " using the 'hasPrevious' and 'previous' methods", + prevCount == expected.length); + + } + + public void test7() { + + String input = "a b c \"d e\" f "; + StrTokenizer tok = new StrTokenizer(input); + tok.setDelimiterMatcher(StrMatcher.spaceMatcher()); + tok.setQuoteMatcher(StrMatcher.doubleQuoteMatcher()); + tok.setIgnoredMatcher(StrMatcher.noneMatcher()); + tok.setIgnoreEmptyTokens(false); + String tokens[] = tok.getTokenArray(); + + String expected[] = new String[]{"a", "", "", "b", "c", "d e", "f", "",}; + + assertEquals(ArrayUtils.toString(tokens), expected.length, tokens.length); + for (int i = 0; i < expected.length; i++) { + assertTrue("token[" + i + "] was '" + tokens[i] + "' but was expected to be '" + expected[i] + "'", + ObjectUtils.equals(expected[i], tokens[i])); + } + + } + + public void test8() { + + String input = "a b c \"d e\" f "; + StrTokenizer tok = new StrTokenizer(input); + tok.setDelimiterMatcher(StrMatcher.spaceMatcher()); + tok.setQuoteMatcher(StrMatcher.doubleQuoteMatcher()); + tok.setIgnoredMatcher(StrMatcher.noneMatcher()); + tok.setIgnoreEmptyTokens(true); + String tokens[] = tok.getTokenArray(); + + String expected[] = new String[]{"a", "b", "c", "d e", "f",}; + + assertEquals(ArrayUtils.toString(tokens), expected.length, tokens.length); + for (int i = 0; i < expected.length; i++) { + assertTrue("token[" + i + "] was '" + tokens[i] + "' but was expected to be '" + expected[i] + "'", + ObjectUtils.equals(expected[i], tokens[i])); + } + + } + + public void testBasic1() { + String input = "a b c"; + StrTokenizer tok = new StrTokenizer(input); + assertEquals("a", tok.next()); + assertEquals("b", tok.next()); + assertEquals("c", tok.next()); + assertEquals(false, tok.hasNext()); + } + + public void testBasic2() { + String input = "a \nb\fc"; + StrTokenizer tok = new StrTokenizer(input); + assertEquals("a", tok.next()); + assertEquals("b", tok.next()); + assertEquals("c", tok.next()); + assertEquals(false, tok.hasNext()); + } + + public void testBasic3() { + String input = "a \nb\u0001\fc"; + StrTokenizer tok = new StrTokenizer(input); + assertEquals("a", tok.next()); + assertEquals("b\u0001", tok.next()); + assertEquals("c", tok.next()); + assertEquals(false, tok.hasNext()); + } + + public void testBasic4() { + String input = "a \"b\" c"; + StrTokenizer tok = new StrTokenizer(input); + assertEquals("a", tok.next()); + assertEquals("\"b\"", tok.next()); + assertEquals("c", tok.next()); + assertEquals(false, tok.hasNext()); + } + + public void testBasic5() { + String input = "a:b':c"; + StrTokenizer tok = new StrTokenizer(input, ':', '\''); + assertEquals("a", tok.next()); + assertEquals("b'", tok.next()); + assertEquals("c", tok.next()); + assertEquals(false, tok.hasNext()); + } + + public void testBasicDelim1() { + String input = "a:b:c"; + StrTokenizer tok = new StrTokenizer(input, ':'); + assertEquals("a", tok.next()); + assertEquals("b", tok.next()); + assertEquals("c", tok.next()); + assertEquals(false, tok.hasNext()); + } + + public void testBasicDelim2() { + String input = "a:b:c"; + StrTokenizer tok = new StrTokenizer(input, ','); + assertEquals("a:b:c", tok.next()); + assertEquals(false, tok.hasNext()); + } + + public void testBasicEmpty1() { + String input = "a b c"; + StrTokenizer tok = new StrTokenizer(input); + tok.setIgnoreEmptyTokens(false); + assertEquals("a", tok.next()); + assertEquals("", tok.next()); + assertEquals("b", tok.next()); + assertEquals("c", tok.next()); + assertEquals(false, tok.hasNext()); + } + + public void testBasicEmpty2() { + String input = "a b c"; + StrTokenizer tok = new StrTokenizer(input); + tok.setIgnoreEmptyTokens(false); + tok.setEmptyTokenAsNull(true); + assertEquals("a", tok.next()); + assertEquals(null, tok.next()); + assertEquals("b", tok.next()); + assertEquals("c", tok.next()); + assertEquals(false, tok.hasNext()); + } + + public void testBasicQuoted1() { + String input = "a 'b' c"; + StrTokenizer tok = new StrTokenizer(input, ' ', '\''); + assertEquals("a", tok.next()); + assertEquals("b", tok.next()); + assertEquals("c", tok.next()); + assertEquals(false, tok.hasNext()); + } + + public void testBasicQuoted2() { + String input = "a:'b':"; + StrTokenizer tok = new StrTokenizer(input, ':', '\''); + tok.setIgnoreEmptyTokens(false); + tok.setEmptyTokenAsNull(true); + assertEquals("a", tok.next()); + assertEquals("b", tok.next()); + assertEquals(null, tok.next()); + assertEquals(false, tok.hasNext()); + } + + public void testBasicQuoted3() { + String input = "a:'b''c'"; + StrTokenizer tok = new StrTokenizer(input, ':', '\''); + tok.setIgnoreEmptyTokens(false); + tok.setEmptyTokenAsNull(true); + assertEquals("a", tok.next()); + assertEquals("b'c", tok.next()); + assertEquals(false, tok.hasNext()); + } + + public void testBasicQuoted4() { + String input = "a: 'b' 'c' :d"; + StrTokenizer tok = new StrTokenizer(input, ':', '\''); + tok.setTrimmerMatcher(StrMatcher.trimMatcher()); + tok.setIgnoreEmptyTokens(false); + tok.setEmptyTokenAsNull(true); + assertEquals("a", tok.next()); + assertEquals("b c", tok.next()); + assertEquals("d", tok.next()); + assertEquals(false, tok.hasNext()); + } + + public void testBasicQuoted5() { + String input = "a: 'b'x'c' :d"; + StrTokenizer tok = new StrTokenizer(input, ':', '\''); + tok.setTrimmerMatcher(StrMatcher.trimMatcher()); + tok.setIgnoreEmptyTokens(false); + tok.setEmptyTokenAsNull(true); + assertEquals("a", tok.next()); + assertEquals("bxc", tok.next()); + assertEquals("d", tok.next()); + assertEquals(false, tok.hasNext()); + } + + public void testBasicQuoted6() { + String input = "a:'b'\"c':d"; + StrTokenizer tok = new StrTokenizer(input, ':'); + tok.setQuoteMatcher(StrMatcher.quoteMatcher()); + assertEquals("a", tok.next()); + assertEquals("b\"c:d", tok.next()); + assertEquals(false, tok.hasNext()); + } + + public void testBasicQuoted7() { + String input = "a:\"There's a reason here\":b"; + StrTokenizer tok = new StrTokenizer(input, ':'); + tok.setQuoteMatcher(StrMatcher.quoteMatcher()); + assertEquals("a", tok.next()); + assertEquals("There's a reason here", tok.next()); + assertEquals("b", tok.next()); + assertEquals(false, tok.hasNext()); + } + + public void testBasicQuotedTrimmed1() { + String input = "a: 'b' :"; + StrTokenizer tok = new StrTokenizer(input, ':', '\''); + tok.setTrimmerMatcher(StrMatcher.trimMatcher()); + tok.setIgnoreEmptyTokens(false); + tok.setEmptyTokenAsNull(true); + assertEquals("a", tok.next()); + assertEquals("b", tok.next()); + assertEquals(null, tok.next()); + assertEquals(false, tok.hasNext()); + } + + public void testBasicTrimmed1() { + String input = "a: b : "; + StrTokenizer tok = new StrTokenizer(input, ':'); + tok.setTrimmerMatcher(StrMatcher.trimMatcher()); + tok.setIgnoreEmptyTokens(false); + tok.setEmptyTokenAsNull(true); + assertEquals("a", tok.next()); + assertEquals("b", tok.next()); + assertEquals(null, tok.next()); + assertEquals(false, tok.hasNext()); + } + + public void testBasicTrimmed2() { + String input = "a: b :"; + StrTokenizer tok = new StrTokenizer(input, ':'); + tok.setTrimmerMatcher(StrMatcher.stringMatcher(" ")); + tok.setIgnoreEmptyTokens(false); + tok.setEmptyTokenAsNull(true); + assertEquals("a", tok.next()); + assertEquals("b", tok.next()); + assertEquals(null, tok.next()); + assertEquals(false, tok.hasNext()); + } + + public void testBasicIgnoreTrimmed1() { + String input = "a: bIGNOREc : "; + StrTokenizer tok = new StrTokenizer(input, ':'); + tok.setIgnoredMatcher(StrMatcher.stringMatcher("IGNORE")); + tok.setTrimmerMatcher(StrMatcher.trimMatcher()); + tok.setIgnoreEmptyTokens(false); + tok.setEmptyTokenAsNull(true); + assertEquals("a", tok.next()); + assertEquals("bc", tok.next()); + assertEquals(null, tok.next()); + assertEquals(false, tok.hasNext()); + } + + public void testBasicIgnoreTrimmed2() { + String input = "IGNOREaIGNORE: IGNORE bIGNOREc IGNORE : IGNORE "; + StrTokenizer tok = new StrTokenizer(input, ':'); + tok.setIgnoredMatcher(StrMatcher.stringMatcher("IGNORE")); + tok.setTrimmerMatcher(StrMatcher.trimMatcher()); + tok.setIgnoreEmptyTokens(false); + tok.setEmptyTokenAsNull(true); + assertEquals("a", tok.next()); + assertEquals("bc", tok.next()); + assertEquals(null, tok.next()); + assertEquals(false, tok.hasNext()); + } + + public void testBasicIgnoreTrimmed3() { + String input = "IGNOREaIGNORE: IGNORE bIGNOREc IGNORE : IGNORE "; + StrTokenizer tok = new StrTokenizer(input, ':'); + tok.setIgnoredMatcher(StrMatcher.stringMatcher("IGNORE")); + tok.setIgnoreEmptyTokens(false); + tok.setEmptyTokenAsNull(true); + assertEquals("a", tok.next()); + assertEquals(" bc ", tok.next()); + assertEquals(" ", tok.next()); + assertEquals(false, tok.hasNext()); + } + + public void testBasicIgnoreTrimmed4() { + String input = "IGNOREaIGNORE: IGNORE 'bIGNOREc'IGNORE'd' IGNORE : IGNORE "; + StrTokenizer tok = new StrTokenizer(input, ':', '\''); + tok.setIgnoredMatcher(StrMatcher.stringMatcher("IGNORE")); + tok.setTrimmerMatcher(StrMatcher.trimMatcher()); + tok.setIgnoreEmptyTokens(false); + tok.setEmptyTokenAsNull(true); + assertEquals("a", tok.next()); + assertEquals("bIGNOREcd", tok.next()); + assertEquals(null, tok.next()); + assertEquals(false, tok.hasNext()); + } + + //----------------------------------------------------------------------- + public void testListArray() { + String input = "a b c"; + StrTokenizer tok = new StrTokenizer(input); + String[] array = tok.getTokenArray(); + List list = tok.getTokenList(); + + assertEquals(Arrays.asList(array), list); + assertEquals(3, list.size()); + } + + //----------------------------------------------------------------------- + public void testCSV(String data) { + this.testXSVAbc(StrTokenizer.getCSVInstance(data)); + this.testXSVAbc(StrTokenizer.getCSVInstance(data.toCharArray())); + } + + public void testCSVEmpty() { + this.testEmpty(StrTokenizer.getCSVInstance()); + this.testEmpty(StrTokenizer.getCSVInstance("")); + } + + public void testCSVSimple() { + this.testCSV(CSV_SIMPLE_FIXTURE); + } + + public void testCSVSimpleNeedsTrim() { + this.testCSV(" " + CSV_SIMPLE_FIXTURE); + this.testCSV(" \n\t " + CSV_SIMPLE_FIXTURE); + this.testCSV(" \n " + CSV_SIMPLE_FIXTURE + "\n\n\r"); + } + + void testEmpty(StrTokenizer tokenizer) { + this.checkClone(tokenizer); + assertEquals(false, tokenizer.hasNext()); + assertEquals(false, tokenizer.hasPrevious()); + assertEquals(null, tokenizer.nextToken()); + assertEquals(0, tokenizer.size()); + try { + tokenizer.next(); + fail(); + } catch (NoSuchElementException ex) {} + } + + public void testGetContent() { + String input = "a b c \"d e\" f "; + StrTokenizer tok = new StrTokenizer(input); + assertEquals(input, tok.getContent()); + + tok = new StrTokenizer(input.toCharArray()); + assertEquals(input, tok.getContent()); + + tok = new StrTokenizer(); + assertEquals(null, tok.getContent()); + } + + //----------------------------------------------------------------------- + public void testChaining() { + StrTokenizer tok = new StrTokenizer(); + assertEquals(tok, tok.reset()); + assertEquals(tok, tok.reset("")); + assertEquals(tok, tok.reset(new char[0])); + assertEquals(tok, tok.setDelimiterChar(' ')); + assertEquals(tok, tok.setDelimiterString(" ")); + assertEquals(tok, tok.setDelimiterMatcher(null)); + assertEquals(tok, tok.setQuoteChar(' ')); + assertEquals(tok, tok.setQuoteMatcher(null)); + assertEquals(tok, tok.setIgnoredChar(' ')); + assertEquals(tok, tok.setIgnoredMatcher(null)); + assertEquals(tok, tok.setTrimmerMatcher(null)); + assertEquals(tok, tok.setEmptyTokenAsNull(false)); + assertEquals(tok, tok.setIgnoreEmptyTokens(false)); + } + + /** + * Tests that the {@link StrTokenizer#clone()} clone method catches {@link CloneNotSupportedException} and returns + * null. + */ + public void testCloneNotSupportedException() { + Object notCloned = new StrTokenizer() { + @Override + Object cloneReset() throws CloneNotSupportedException { + throw new CloneNotSupportedException("test"); + } + }.clone(); + assertNull(notCloned); + } + + public void testCloneNull() { + StrTokenizer tokenizer = new StrTokenizer((char[]) null); + // Start sanity check + assertEquals(null, tokenizer.nextToken()); + tokenizer.reset(); + assertEquals(null, tokenizer.nextToken()); + // End sanity check + StrTokenizer clonedTokenizer = (StrTokenizer) tokenizer.clone(); + tokenizer.reset(); + assertEquals(null, tokenizer.nextToken()); + assertEquals(null, clonedTokenizer.nextToken()); + } + + public void testCloneReset() { + char[] input = new char[]{'a'}; + StrTokenizer tokenizer = new StrTokenizer(input); + // Start sanity check + assertEquals("a", tokenizer.nextToken()); + tokenizer.reset(input); + assertEquals("a", tokenizer.nextToken()); + // End sanity check + StrTokenizer clonedTokenizer = (StrTokenizer) tokenizer.clone(); + input[0] = 'b'; + tokenizer.reset(input); + assertEquals("b", tokenizer.nextToken()); + assertEquals("a", clonedTokenizer.nextToken()); + } + + // ----------------------------------------------------------------------- + public void testConstructor_String() { + StrTokenizer tok = new StrTokenizer("a b"); + assertEquals("a", tok.next()); + assertEquals("b", tok.next()); + assertEquals(false, tok.hasNext()); + + tok = new StrTokenizer(""); + assertEquals(false, tok.hasNext()); + + tok = new StrTokenizer((String) null); + assertEquals(false, tok.hasNext()); + } + + //----------------------------------------------------------------------- + public void testConstructor_String_char() { + StrTokenizer tok = new StrTokenizer("a b", ' '); + assertEquals(1, tok.getDelimiterMatcher().isMatch(" ".toCharArray(), 0, 0, 1)); + assertEquals("a", tok.next()); + assertEquals("b", tok.next()); + assertEquals(false, tok.hasNext()); + + tok = new StrTokenizer("", ' '); + assertEquals(false, tok.hasNext()); + + tok = new StrTokenizer((String) null, ' '); + assertEquals(false, tok.hasNext()); + } + + //----------------------------------------------------------------------- + public void testConstructor_String_char_char() { + StrTokenizer tok = new StrTokenizer("a b", ' ', '"'); + assertEquals(1, tok.getDelimiterMatcher().isMatch(" ".toCharArray(), 0, 0, 1)); + assertEquals(1, tok.getQuoteMatcher().isMatch("\"".toCharArray(), 0, 0, 1)); + assertEquals("a", tok.next()); + assertEquals("b", tok.next()); + assertEquals(false, tok.hasNext()); + + tok = new StrTokenizer("", ' ', '"'); + assertEquals(false, tok.hasNext()); + + tok = new StrTokenizer((String) null, ' ', '"'); + assertEquals(false, tok.hasNext()); + } + + //----------------------------------------------------------------------- + public void testConstructor_charArray() { + StrTokenizer tok = new StrTokenizer("a b".toCharArray()); + assertEquals("a", tok.next()); + assertEquals("b", tok.next()); + assertEquals(false, tok.hasNext()); + + tok = new StrTokenizer(new char[0]); + assertEquals(false, tok.hasNext()); + + tok = new StrTokenizer((char[]) null); + assertEquals(false, tok.hasNext()); + } + + //----------------------------------------------------------------------- + public void testConstructor_charArray_char() { + StrTokenizer tok = new StrTokenizer("a b".toCharArray(), ' '); + assertEquals(1, tok.getDelimiterMatcher().isMatch(" ".toCharArray(), 0, 0, 1)); + assertEquals("a", tok.next()); + assertEquals("b", tok.next()); + assertEquals(false, tok.hasNext()); + + tok = new StrTokenizer(new char[0], ' '); + assertEquals(false, tok.hasNext()); + + tok = new StrTokenizer((char[]) null, ' '); + assertEquals(false, tok.hasNext()); + } + + //----------------------------------------------------------------------- + public void testConstructor_charArray_char_char() { + StrTokenizer tok = new StrTokenizer("a b".toCharArray(), ' ', '"'); + assertEquals(1, tok.getDelimiterMatcher().isMatch(" ".toCharArray(), 0, 0, 1)); + assertEquals(1, tok.getQuoteMatcher().isMatch("\"".toCharArray(), 0, 0, 1)); + assertEquals("a", tok.next()); + assertEquals("b", tok.next()); + assertEquals(false, tok.hasNext()); + + tok = new StrTokenizer(new char[0], ' ', '"'); + assertEquals(false, tok.hasNext()); + + tok = new StrTokenizer((char[]) null, ' ', '"'); + assertEquals(false, tok.hasNext()); + } + + //----------------------------------------------------------------------- + public void testReset() { + StrTokenizer tok = new StrTokenizer("a b c"); + assertEquals("a", tok.next()); + assertEquals("b", tok.next()); + assertEquals("c", tok.next()); + assertEquals(false, tok.hasNext()); + + tok.reset(); + assertEquals("a", tok.next()); + assertEquals("b", tok.next()); + assertEquals("c", tok.next()); + assertEquals(false, tok.hasNext()); + } + + //----------------------------------------------------------------------- + public void testReset_String() { + StrTokenizer tok = new StrTokenizer("x x x"); + tok.reset("d e"); + assertEquals("d", tok.next()); + assertEquals("e", tok.next()); + assertEquals(false, tok.hasNext()); + + tok.reset((String) null); + assertEquals(false, tok.hasNext()); + } + + //----------------------------------------------------------------------- + public void testReset_charArray() { + StrTokenizer tok = new StrTokenizer("x x x"); + + char[] array = new char[] {'a', 'b', 'c'}; + tok.reset(array); + assertEquals("abc", tok.next()); + assertEquals(false, tok.hasNext()); + + tok.reset((char[]) null); + assertEquals(false, tok.hasNext()); + } + + //----------------------------------------------------------------------- + public void testTSV() { + this.testXSVAbc(StrTokenizer.getTSVInstance(TSV_SIMPLE_FIXTURE)); + this.testXSVAbc(StrTokenizer.getTSVInstance(TSV_SIMPLE_FIXTURE.toCharArray())); + } + + public void testTSVEmpty() { + this.testEmpty(StrTokenizer.getCSVInstance()); + this.testEmpty(StrTokenizer.getCSVInstance("")); + } + + void testXSVAbc(StrTokenizer tokenizer) { + this.checkClone(tokenizer); + assertEquals(-1, tokenizer.previousIndex()); + assertEquals(0, tokenizer.nextIndex()); + assertEquals(null, tokenizer.previousToken()); + assertEquals("A", tokenizer.nextToken()); + assertEquals(1, tokenizer.nextIndex()); + assertEquals("b", tokenizer.nextToken()); + assertEquals(2, tokenizer.nextIndex()); + assertEquals("c", tokenizer.nextToken()); + assertEquals(3, tokenizer.nextIndex()); + assertEquals(null, tokenizer.nextToken()); + assertEquals(3, tokenizer.nextIndex()); + assertEquals("c", tokenizer.previousToken()); + assertEquals(2, tokenizer.nextIndex()); + assertEquals("b", tokenizer.previousToken()); + assertEquals(1, tokenizer.nextIndex()); + assertEquals("A", tokenizer.previousToken()); + assertEquals(0, tokenizer.nextIndex()); + assertEquals(null, tokenizer.previousToken()); + assertEquals(0, tokenizer.nextIndex()); + assertEquals(-1, tokenizer.previousIndex()); + assertEquals(3, tokenizer.size()); + } + + public void testIteration() { + StrTokenizer tkn = new StrTokenizer("a b c"); + assertEquals(false, tkn.hasPrevious()); + try { + tkn.previous(); + fail(); + } catch (NoSuchElementException ex) {} + assertEquals(true, tkn.hasNext()); + + assertEquals("a", tkn.next()); + try { + tkn.remove(); + fail(); + } catch (UnsupportedOperationException ex) {} + try { + tkn.set("x"); + fail(); + } catch (UnsupportedOperationException ex) {} + try { + tkn.add("y"); + fail(); + } catch (UnsupportedOperationException ex) {} + assertEquals(true, tkn.hasPrevious()); + assertEquals(true, tkn.hasNext()); + + assertEquals("b", tkn.next()); + assertEquals(true, tkn.hasPrevious()); + assertEquals(true, tkn.hasNext()); + + assertEquals("c", tkn.next()); + assertEquals(true, tkn.hasPrevious()); + assertEquals(false, tkn.hasNext()); + + try { + tkn.next(); + fail(); + } catch (NoSuchElementException ex) {} + assertEquals(true, tkn.hasPrevious()); + assertEquals(false, tkn.hasNext()); + } + + //----------------------------------------------------------------------- + public void testTokenizeSubclassInputChange() { + StrTokenizer tkn = new StrTokenizer("a b c d e") { + @Override + protected List tokenize(char[] chars, int offset, int count) { + return super.tokenize("w x y z".toCharArray(), 2, 5); + } + }; + assertEquals("x", tkn.next()); + assertEquals("y", tkn.next()); + } + + //----------------------------------------------------------------------- + public void testTokenizeSubclassOutputChange() { + StrTokenizer tkn = new StrTokenizer("a b c") { + @Override + protected List tokenize(char[] chars, int offset, int count) { + List list = super.tokenize(chars, offset, count); + Collections.reverse(list); + return list; + } + }; + assertEquals("c", tkn.next()); + assertEquals("b", tkn.next()); + assertEquals("a", tkn.next()); + } + + //----------------------------------------------------------------------- + public void testToString() { + StrTokenizer tkn = new StrTokenizer("a b c d e"); + assertEquals("StrTokenizer[not tokenized yet]", tkn.toString()); + tkn.next(); + assertEquals("StrTokenizer[a, b, c, d, e]", tkn.toString()); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/text/WordUtils.java b/ApacheCommonsLang/org/apache/commons/lang3/text/WordUtils.java new file mode 100644 index 0000000..22d0079 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/text/WordUtils.java @@ -0,0 +1,497 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.text; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.SystemUtils; + +/** + *

    Operations on Strings that contain words.

    + * + *

    This class tries to handle null input gracefully. + * An exception will not be thrown for a null input. + * Each method documents its behaviour in more detail.

    + * + * @since 2.0 + * @version $Id: WordUtils.java 1199894 2011-11-09 17:53:59Z ggregory $ + */ +public class WordUtils { + + /** + *

    WordUtils instances should NOT be constructed in + * standard programming. Instead, the class should be used as + * WordUtils.wrap("foo bar", 20);.

    + * + *

    This constructor is public to permit tools that require a JavaBean + * instance to operate.

    + */ + public WordUtils() { + super(); + } + + // Wrapping + //-------------------------------------------------------------------------- + /** + *

    Wraps a single line of text, identifying words by ' '.

    + * + *

    New lines will be separated by the system property line separator. + * Very long words, such as URLs will not be wrapped.

    + * + *

    Leading spaces on a new line are stripped. + * Trailing spaces are not stripped.

    + * + *
    +     * WordUtils.wrap(null, *) = null
    +     * WordUtils.wrap("", *) = ""
    +     * 
    + * + * @param str the String to be word wrapped, may be null + * @param wrapLength the column to wrap the words at, less than 1 is treated as 1 + * @return a line with newlines inserted, null if null input + */ + public static String wrap(String str, int wrapLength) { + return wrap(str, wrapLength, null, false); + } + + /** + *

    Wraps a single line of text, identifying words by ' '.

    + * + *

    Leading spaces on a new line are stripped. + * Trailing spaces are not stripped.

    + * + *
    +     * WordUtils.wrap(null, *, *, *) = null
    +     * WordUtils.wrap("", *, *, *) = ""
    +     * 
    + * + * @param str the String to be word wrapped, may be null + * @param wrapLength the column to wrap the words at, less than 1 is treated as 1 + * @param newLineStr the string to insert for a new line, + * null uses the system property line separator + * @param wrapLongWords true if long words (such as URLs) should be wrapped + * @return a line with newlines inserted, null if null input + */ + public static String wrap(String str, int wrapLength, String newLineStr, boolean wrapLongWords) { + if (str == null) { + return null; + } + if (newLineStr == null) { + newLineStr = SystemUtils.LINE_SEPARATOR; + } + if (wrapLength < 1) { + wrapLength = 1; + } + int inputLineLength = str.length(); + int offset = 0; + StringBuilder wrappedLine = new StringBuilder(inputLineLength + 32); + + while (inputLineLength - offset > wrapLength) { + if (str.charAt(offset) == ' ') { + offset++; + continue; + } + int spaceToWrapAt = str.lastIndexOf(' ', wrapLength + offset); + + if (spaceToWrapAt >= offset) { + // normal case + wrappedLine.append(str.substring(offset, spaceToWrapAt)); + wrappedLine.append(newLineStr); + offset = spaceToWrapAt + 1; + + } else { + // really long word or URL + if (wrapLongWords) { + // wrap really long word one line at a time + wrappedLine.append(str.substring(offset, wrapLength + offset)); + wrappedLine.append(newLineStr); + offset += wrapLength; + } else { + // do not wrap really long word, just extend beyond limit + spaceToWrapAt = str.indexOf(' ', wrapLength + offset); + if (spaceToWrapAt >= 0) { + wrappedLine.append(str.substring(offset, spaceToWrapAt)); + wrappedLine.append(newLineStr); + offset = spaceToWrapAt + 1; + } else { + wrappedLine.append(str.substring(offset)); + offset = inputLineLength; + } + } + } + } + + // Whatever is left in line is short enough to just pass through + wrappedLine.append(str.substring(offset)); + + return wrappedLine.toString(); + } + + // Capitalizing + //----------------------------------------------------------------------- + /** + *

    Capitalizes all the whitespace separated words in a String. + * Only the first letter of each word is changed. To convert the + * rest of each word to lowercase at the same time, + * use {@link #capitalizeFully(String)}.

    + * + *

    Whitespace is defined by {@link Character#isWhitespace(char)}. + * A null input String returns null. + * Capitalization uses the Unicode title case, normally equivalent to + * upper case.

    + * + *
    +     * WordUtils.capitalize(null)        = null
    +     * WordUtils.capitalize("")          = ""
    +     * WordUtils.capitalize("i am FINE") = "I Am FINE"
    +     * 
    + * + * @param str the String to capitalize, may be null + * @return capitalized String, null if null String input + * @see #uncapitalize(String) + * @see #capitalizeFully(String) + */ + public static String capitalize(String str) { + return capitalize(str, null); + } + + /** + *

    Capitalizes all the delimiter separated words in a String. + * Only the first letter of each word is changed. To convert the + * rest of each word to lowercase at the same time, + * use {@link #capitalizeFully(String, char[])}.

    + * + *

    The delimiters represent a set of characters understood to separate words. + * The first string character and the first non-delimiter character after a + * delimiter will be capitalized.

    + * + *

    A null input String returns null. + * Capitalization uses the Unicode title case, normally equivalent to + * upper case.

    + * + *
    +     * WordUtils.capitalize(null, *)            = null
    +     * WordUtils.capitalize("", *)              = ""
    +     * WordUtils.capitalize(*, new char[0])     = *
    +     * WordUtils.capitalize("i am fine", null)  = "I Am Fine"
    +     * WordUtils.capitalize("i aM.fine", {'.'}) = "I aM.Fine"
    +     * 
    + * + * @param str the String to capitalize, may be null + * @param delimiters set of characters to determine capitalization, null means whitespace + * @return capitalized String, null if null String input + * @see #uncapitalize(String) + * @see #capitalizeFully(String) + * @since 2.1 + */ + public static String capitalize(String str, char... delimiters) { + int delimLen = delimiters == null ? -1 : delimiters.length; + if (StringUtils.isEmpty(str) || delimLen == 0) { + return str; + } + char[] buffer = str.toCharArray(); + boolean capitalizeNext = true; + for (int i = 0; i < buffer.length; i++) { + char ch = buffer[i]; + if (isDelimiter(ch, delimiters)) { + capitalizeNext = true; + } else if (capitalizeNext) { + buffer[i] = Character.toTitleCase(ch); + capitalizeNext = false; + } + } + return new String(buffer); + } + + //----------------------------------------------------------------------- + /** + *

    Converts all the whitespace separated words in a String into capitalized words, + * that is each word is made up of a titlecase character and then a series of + * lowercase characters.

    + * + *

    Whitespace is defined by {@link Character#isWhitespace(char)}. + * A null input String returns null. + * Capitalization uses the Unicode title case, normally equivalent to + * upper case.

    + * + *
    +     * WordUtils.capitalizeFully(null)        = null
    +     * WordUtils.capitalizeFully("")          = ""
    +     * WordUtils.capitalizeFully("i am FINE") = "I Am Fine"
    +     * 
    + * + * @param str the String to capitalize, may be null + * @return capitalized String, null if null String input + */ + public static String capitalizeFully(String str) { + return capitalizeFully(str, null); + } + + /** + *

    Converts all the delimiter separated words in a String into capitalized words, + * that is each word is made up of a titlecase character and then a series of + * lowercase characters.

    + * + *

    The delimiters represent a set of characters understood to separate words. + * The first string character and the first non-delimiter character after a + * delimiter will be capitalized.

    + * + *

    A null input String returns null. + * Capitalization uses the Unicode title case, normally equivalent to + * upper case.

    + * + *
    +     * WordUtils.capitalizeFully(null, *)            = null
    +     * WordUtils.capitalizeFully("", *)              = ""
    +     * WordUtils.capitalizeFully(*, null)            = *
    +     * WordUtils.capitalizeFully(*, new char[0])     = *
    +     * WordUtils.capitalizeFully("i aM.fine", {'.'}) = "I am.Fine"
    +     * 
    + * + * @param str the String to capitalize, may be null + * @param delimiters set of characters to determine capitalization, null means whitespace + * @return capitalized String, null if null String input + * @since 2.1 + */ + public static String capitalizeFully(String str, char... delimiters) { + int delimLen = delimiters == null ? -1 : delimiters.length; + if (StringUtils.isEmpty(str) || delimLen == 0) { + return str; + } + str = str.toLowerCase(); + return capitalize(str, delimiters); + } + + //----------------------------------------------------------------------- + /** + *

    Uncapitalizes all the whitespace separated words in a String. + * Only the first letter of each word is changed.

    + * + *

    Whitespace is defined by {@link Character#isWhitespace(char)}. + * A null input String returns null.

    + * + *
    +     * WordUtils.uncapitalize(null)        = null
    +     * WordUtils.uncapitalize("")          = ""
    +     * WordUtils.uncapitalize("I Am FINE") = "i am fINE"
    +     * 
    + * + * @param str the String to uncapitalize, may be null + * @return uncapitalized String, null if null String input + * @see #capitalize(String) + */ + public static String uncapitalize(String str) { + return uncapitalize(str, null); + } + + /** + *

    Uncapitalizes all the whitespace separated words in a String. + * Only the first letter of each word is changed.

    + * + *

    The delimiters represent a set of characters understood to separate words. + * The first string character and the first non-delimiter character after a + * delimiter will be uncapitalized.

    + * + *

    Whitespace is defined by {@link Character#isWhitespace(char)}. + * A null input String returns null.

    + * + *
    +     * WordUtils.uncapitalize(null, *)            = null
    +     * WordUtils.uncapitalize("", *)              = ""
    +     * WordUtils.uncapitalize(*, null)            = *
    +     * WordUtils.uncapitalize(*, new char[0])     = *
    +     * WordUtils.uncapitalize("I AM.FINE", {'.'}) = "i AM.fINE"
    +     * 
    + * + * @param str the String to uncapitalize, may be null + * @param delimiters set of characters to determine uncapitalization, null means whitespace + * @return uncapitalized String, null if null String input + * @see #capitalize(String) + * @since 2.1 + */ + public static String uncapitalize(String str, char... delimiters) { + int delimLen = delimiters == null ? -1 : delimiters.length; + if (StringUtils.isEmpty(str) || delimLen == 0) { + return str; + } + char[] buffer = str.toCharArray(); + boolean uncapitalizeNext = true; + for (int i = 0; i < buffer.length; i++) { + char ch = buffer[i]; + if (isDelimiter(ch, delimiters)) { + uncapitalizeNext = true; + } else if (uncapitalizeNext) { + buffer[i] = Character.toLowerCase(ch); + uncapitalizeNext = false; + } + } + return new String(buffer); + } + + //----------------------------------------------------------------------- + /** + *

    Swaps the case of a String using a word based algorithm.

    + * + *
      + *
    • Upper case character converts to Lower case
    • + *
    • Title case character converts to Lower case
    • + *
    • Lower case character after Whitespace or at start converts to Title case
    • + *
    • Other Lower case character converts to Upper case
    • + *
    + * + *

    Whitespace is defined by {@link Character#isWhitespace(char)}. + * A null input String returns null.

    + * + *
    +     * StringUtils.swapCase(null)                 = null
    +     * StringUtils.swapCase("")                   = ""
    +     * StringUtils.swapCase("The dog has a BONE") = "tHE DOG HAS A bone"
    +     * 
    + * + * @param str the String to swap case, may be null + * @return the changed String, null if null String input + */ + public static String swapCase(String str) { + if (StringUtils.isEmpty(str)) { + return str; + } + char[] buffer = str.toCharArray(); + + boolean whitespace = true; + + for (int i = 0; i < buffer.length; i++) { + char ch = buffer[i]; + if (Character.isUpperCase(ch)) { + buffer[i] = Character.toLowerCase(ch); + whitespace = false; + } else if (Character.isTitleCase(ch)) { + buffer[i] = Character.toLowerCase(ch); + whitespace = false; + } else if (Character.isLowerCase(ch)) { + if (whitespace) { + buffer[i] = Character.toTitleCase(ch); + whitespace = false; + } else { + buffer[i] = Character.toUpperCase(ch); + } + } else { + whitespace = Character.isWhitespace(ch); + } + } + return new String(buffer); + } + + //----------------------------------------------------------------------- + /** + *

    Extracts the initial letters from each word in the String.

    + * + *

    The first letter of the string and all first letters after + * whitespace are returned as a new string. + * Their case is not changed.

    + * + *

    Whitespace is defined by {@link Character#isWhitespace(char)}. + * A null input String returns null.

    + * + *
    +     * WordUtils.initials(null)             = null
    +     * WordUtils.initials("")               = ""
    +     * WordUtils.initials("Ben John Lee")   = "BJL"
    +     * WordUtils.initials("Ben J.Lee")      = "BJ"
    +     * 
    + * + * @param str the String to get initials from, may be null + * @return String of initial letters, null if null String input + * @see #initials(String,char[]) + * @since 2.2 + */ + public static String initials(String str) { + return initials(str, null); + } + + /** + *

    Extracts the initial letters from each word in the String.

    + * + *

    The first letter of the string and all first letters after the + * defined delimiters are returned as a new string. + * Their case is not changed.

    + * + *

    If the delimiters array is null, then Whitespace is used. + * Whitespace is defined by {@link Character#isWhitespace(char)}. + * A null input String returns null. + * An empty delimiter array returns an empty String.

    + * + *
    +     * WordUtils.initials(null, *)                = null
    +     * WordUtils.initials("", *)                  = ""
    +     * WordUtils.initials("Ben John Lee", null)   = "BJL"
    +     * WordUtils.initials("Ben J.Lee", null)      = "BJ"
    +     * WordUtils.initials("Ben J.Lee", [' ','.']) = "BJL"
    +     * WordUtils.initials(*, new char[0])         = ""
    +     * 
    + * + * @param str the String to get initials from, may be null + * @param delimiters set of characters to determine words, null means whitespace + * @return String of initial letters, null if null String input + * @see #initials(String) + * @since 2.2 + */ + public static String initials(String str, char... delimiters) { + if (StringUtils.isEmpty(str)) { + return str; + } + if (delimiters != null && delimiters.length == 0) { + return ""; + } + int strLen = str.length(); + char[] buf = new char[strLen / 2 + 1]; + int count = 0; + boolean lastWasGap = true; + for (int i = 0; i < strLen; i++) { + char ch = str.charAt(i); + + if (isDelimiter(ch, delimiters)) { + lastWasGap = true; + } else if (lastWasGap) { + buf[count++] = ch; + lastWasGap = false; + } else { + continue; // ignore ch + } + } + return new String(buf, 0, count); + } + + //----------------------------------------------------------------------- + /** + * Is the character a delimiter. + * + * @param ch the character to check + * @param delimiters the delimiters + * @return true if it is a delimiter + */ + private static boolean isDelimiter(char ch, char[] delimiters) { + if (delimiters == null) { + return Character.isWhitespace(ch); + } + for (char delimiter : delimiters) { + if (ch == delimiter) { + return true; + } + } + return false; + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/text/WordUtilsTest.java b/ApacheCommonsLang/org/apache/commons/lang3/text/WordUtilsTest.java new file mode 100644 index 0000000..1f735aa --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/text/WordUtilsTest.java @@ -0,0 +1,355 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.text; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Modifier; + +import junit.framework.TestCase; + +/** + * Unit tests for WordUtils class. + * + * @version $Id: WordUtilsTest.java 1088899 2011-04-05 05:31:27Z bayard $ + */ +public class WordUtilsTest extends TestCase { + + public WordUtilsTest(String name) { + super(name); + } + + //----------------------------------------------------------------------- + public void testConstructor() { + assertNotNull(new WordUtils()); + Constructor[] cons = WordUtils.class.getDeclaredConstructors(); + assertEquals(1, cons.length); + assertEquals(true, Modifier.isPublic(cons[0].getModifiers())); + assertEquals(true, Modifier.isPublic(WordUtils.class.getModifiers())); + assertEquals(false, Modifier.isFinal(WordUtils.class.getModifiers())); + } + + //----------------------------------------------------------------------- + public void testWrap_StringInt() { + assertEquals(null, WordUtils.wrap(null, 20)); + assertEquals(null, WordUtils.wrap(null, -1)); + + assertEquals("", WordUtils.wrap("", 20)); + assertEquals("", WordUtils.wrap("", -1)); + + // normal + String systemNewLine = System.getProperty("line.separator"); + String input = "Here is one line of text that is going to be wrapped after 20 columns."; + String expected = "Here is one line of" + systemNewLine + "text that is going" + + systemNewLine + "to be wrapped after" + systemNewLine + "20 columns."; + assertEquals(expected, WordUtils.wrap(input, 20)); + + // long word at end + input = "Click here to jump to the jakarta website - http://jakarta.apache.org"; + expected = "Click here to jump" + systemNewLine + "to the jakarta" + systemNewLine + + "website -" + systemNewLine + "http://jakarta.apache.org"; + assertEquals(expected, WordUtils.wrap(input, 20)); + + // long word in middle + input = "Click here, http://jakarta.apache.org, to jump to the jakarta website"; + expected = "Click here," + systemNewLine + "http://jakarta.apache.org," + systemNewLine + + "to jump to the" + systemNewLine + "jakarta website"; + assertEquals(expected, WordUtils.wrap(input, 20)); + } + + public void testWrap_StringIntStringBoolean() { + assertEquals(null, WordUtils.wrap(null, 20, "\n", false)); + assertEquals(null, WordUtils.wrap(null, 20, "\n", true)); + assertEquals(null, WordUtils.wrap(null, 20, null, true)); + assertEquals(null, WordUtils.wrap(null, 20, null, false)); + assertEquals(null, WordUtils.wrap(null, -1, null, true)); + assertEquals(null, WordUtils.wrap(null, -1, null, false)); + + assertEquals("", WordUtils.wrap("", 20, "\n", false)); + assertEquals("", WordUtils.wrap("", 20, "\n", true)); + assertEquals("", WordUtils.wrap("", 20, null, false)); + assertEquals("", WordUtils.wrap("", 20, null, true)); + assertEquals("", WordUtils.wrap("", -1, null, false)); + assertEquals("", WordUtils.wrap("", -1, null, true)); + + // normal + String input = "Here is one line of text that is going to be wrapped after 20 columns."; + String expected = "Here is one line of\ntext that is going\nto be wrapped after\n20 columns."; + assertEquals(expected, WordUtils.wrap(input, 20, "\n", false)); + assertEquals(expected, WordUtils.wrap(input, 20, "\n", true)); + + // unusual newline char + input = "Here is one line of text that is going to be wrapped after 20 columns."; + expected = "Here is one line of
    text that is going
    to be wrapped after
    20 columns."; + assertEquals(expected, WordUtils.wrap(input, 20, "
    ", false)); + assertEquals(expected, WordUtils.wrap(input, 20, "
    ", true)); + + // short line length + input = "Here is one line"; + expected = "Here\nis one\nline"; + assertEquals(expected, WordUtils.wrap(input, 6, "\n", false)); + expected = "Here\nis\none\nline"; + assertEquals(expected, WordUtils.wrap(input, 2, "\n", false)); + assertEquals(expected, WordUtils.wrap(input, -1, "\n", false)); + + // system newline char + String systemNewLine = System.getProperty("line.separator"); + input = "Here is one line of text that is going to be wrapped after 20 columns."; + expected = "Here is one line of" + systemNewLine + "text that is going" + systemNewLine + + "to be wrapped after" + systemNewLine + "20 columns."; + assertEquals(expected, WordUtils.wrap(input, 20, null, false)); + assertEquals(expected, WordUtils.wrap(input, 20, null, true)); + + // with extra spaces + input = " Here: is one line of text that is going to be wrapped after 20 columns."; + expected = "Here: is one line\nof text that is \ngoing to be \nwrapped after 20 \ncolumns."; + assertEquals(expected, WordUtils.wrap(input, 20, "\n", false)); + assertEquals(expected, WordUtils.wrap(input, 20, "\n", true)); + + // with tab + input = "Here is\tone line of text that is going to be wrapped after 20 columns."; + expected = "Here is\tone line of\ntext that is going\nto be wrapped after\n20 columns."; + assertEquals(expected, WordUtils.wrap(input, 20, "\n", false)); + assertEquals(expected, WordUtils.wrap(input, 20, "\n", true)); + + // with tab at wrapColumn + input = "Here is one line of\ttext that is going to be wrapped after 20 columns."; + expected = "Here is one line\nof\ttext that is\ngoing to be wrapped\nafter 20 columns."; + assertEquals(expected, WordUtils.wrap(input, 20, "\n", false)); + assertEquals(expected, WordUtils.wrap(input, 20, "\n", true)); + + // difference because of long word + input = "Click here to jump to the jakarta website - http://jakarta.apache.org"; + expected = "Click here to jump\nto the jakarta\nwebsite -\nhttp://jakarta.apache.org"; + assertEquals(expected, WordUtils.wrap(input, 20, "\n", false)); + expected = "Click here to jump\nto the jakarta\nwebsite -\nhttp://jakarta.apach\ne.org"; + assertEquals(expected, WordUtils.wrap(input, 20, "\n", true)); + + // difference because of long word in middle + input = "Click here, http://jakarta.apache.org, to jump to the jakarta website"; + expected = "Click here,\nhttp://jakarta.apache.org,\nto jump to the\njakarta website"; + assertEquals(expected, WordUtils.wrap(input, 20, "\n", false)); + expected = "Click here,\nhttp://jakarta.apach\ne.org, to jump to\nthe jakarta website"; + assertEquals(expected, WordUtils.wrap(input, 20, "\n", true)); +// System.err.println(expected); +// System.err.println(WordUtils.wrap(input, 20, "\n", false)); + } + + //----------------------------------------------------------------------- + public void testCapitalize_String() { + assertEquals(null, WordUtils.capitalize(null)); + assertEquals("", WordUtils.capitalize("")); + assertEquals(" ", WordUtils.capitalize(" ")); + + assertEquals("I", WordUtils.capitalize("I") ); + assertEquals("I", WordUtils.capitalize("i") ); + assertEquals("I Am Here 123", WordUtils.capitalize("i am here 123") ); + assertEquals("I Am Here 123", WordUtils.capitalize("I Am Here 123") ); + assertEquals("I Am HERE 123", WordUtils.capitalize("i am HERE 123") ); + assertEquals("I AM HERE 123", WordUtils.capitalize("I AM HERE 123") ); + } + + public void testCapitalizeWithDelimiters_String() { + assertEquals(null, WordUtils.capitalize(null, null)); + assertEquals("", WordUtils.capitalize("", new char[0])); + assertEquals(" ", WordUtils.capitalize(" ", new char[0])); + + char[] chars = new char[] { '-', '+', ' ', '@' }; + assertEquals("I", WordUtils.capitalize("I", chars) ); + assertEquals("I", WordUtils.capitalize("i", chars) ); + assertEquals("I-Am Here+123", WordUtils.capitalize("i-am here+123", chars) ); + assertEquals("I Am+Here-123", WordUtils.capitalize("I Am+Here-123", chars) ); + assertEquals("I+Am-HERE 123", WordUtils.capitalize("i+am-HERE 123", chars) ); + assertEquals("I-AM HERE+123", WordUtils.capitalize("I-AM HERE+123", chars) ); + chars = new char[] {'.'}; + assertEquals("I aM.Fine", WordUtils.capitalize("i aM.fine", chars) ); + assertEquals("I Am.fine", WordUtils.capitalize("i am.fine", null) ); + } + + public void testCapitalizeFully_String() { + assertEquals(null, WordUtils.capitalizeFully(null)); + assertEquals("", WordUtils.capitalizeFully("")); + assertEquals(" ", WordUtils.capitalizeFully(" ")); + + assertEquals("I", WordUtils.capitalizeFully("I") ); + assertEquals("I", WordUtils.capitalizeFully("i") ); + assertEquals("I Am Here 123", WordUtils.capitalizeFully("i am here 123") ); + assertEquals("I Am Here 123", WordUtils.capitalizeFully("I Am Here 123") ); + assertEquals("I Am Here 123", WordUtils.capitalizeFully("i am HERE 123") ); + assertEquals("I Am Here 123", WordUtils.capitalizeFully("I AM HERE 123") ); + } + + public void testCapitalizeFullyWithDelimiters_String() { + assertEquals(null, WordUtils.capitalizeFully(null, null)); + assertEquals("", WordUtils.capitalizeFully("", new char[0])); + assertEquals(" ", WordUtils.capitalizeFully(" ", new char[0])); + + char[] chars = new char[] { '-', '+', ' ', '@' }; + assertEquals("I", WordUtils.capitalizeFully("I", chars) ); + assertEquals("I", WordUtils.capitalizeFully("i", chars) ); + assertEquals("I-Am Here+123", WordUtils.capitalizeFully("i-am here+123", chars) ); + assertEquals("I Am+Here-123", WordUtils.capitalizeFully("I Am+Here-123", chars) ); + assertEquals("I+Am-Here 123", WordUtils.capitalizeFully("i+am-HERE 123", chars) ); + assertEquals("I-Am Here+123", WordUtils.capitalizeFully("I-AM HERE+123", chars) ); + chars = new char[] {'.'}; + assertEquals("I am.Fine", WordUtils.capitalizeFully("i aM.fine", chars) ); + assertEquals("I Am.fine", WordUtils.capitalizeFully("i am.fine", null) ); + } + + public void testUncapitalize_String() { + assertEquals(null, WordUtils.uncapitalize(null)); + assertEquals("", WordUtils.uncapitalize("")); + assertEquals(" ", WordUtils.uncapitalize(" ")); + + assertEquals("i", WordUtils.uncapitalize("I") ); + assertEquals("i", WordUtils.uncapitalize("i") ); + assertEquals("i am here 123", WordUtils.uncapitalize("i am here 123") ); + assertEquals("i am here 123", WordUtils.uncapitalize("I Am Here 123") ); + assertEquals("i am hERE 123", WordUtils.uncapitalize("i am HERE 123") ); + assertEquals("i aM hERE 123", WordUtils.uncapitalize("I AM HERE 123") ); + } + + public void testUncapitalizeWithDelimiters_String() { + assertEquals(null, WordUtils.uncapitalize(null, null)); + assertEquals("", WordUtils.uncapitalize("", new char[0])); + assertEquals(" ", WordUtils.uncapitalize(" ", new char[0])); + + char[] chars = new char[] { '-', '+', ' ', '@' }; + assertEquals("i", WordUtils.uncapitalize("I", chars) ); + assertEquals("i", WordUtils.uncapitalize("i", chars) ); + assertEquals("i am-here+123", WordUtils.uncapitalize("i am-here+123", chars) ); + assertEquals("i+am here-123", WordUtils.uncapitalize("I+Am Here-123", chars) ); + assertEquals("i-am+hERE 123", WordUtils.uncapitalize("i-am+HERE 123", chars) ); + assertEquals("i aM-hERE+123", WordUtils.uncapitalize("I AM-HERE+123", chars) ); + chars = new char[] {'.'}; + assertEquals("i AM.fINE", WordUtils.uncapitalize("I AM.FINE", chars) ); + assertEquals("i aM.FINE", WordUtils.uncapitalize("I AM.FINE", null) ); + } + + //----------------------------------------------------------------------- + public void testInitials_String() { + assertEquals(null, WordUtils.initials(null)); + assertEquals("", WordUtils.initials("")); + assertEquals("", WordUtils.initials(" ")); + + assertEquals("I", WordUtils.initials("I")); + assertEquals("i", WordUtils.initials("i")); + assertEquals("BJL", WordUtils.initials("Ben John Lee")); + assertEquals("BJ", WordUtils.initials("Ben J.Lee")); + assertEquals("BJ.L", WordUtils.initials(" Ben John . Lee")); + assertEquals("iah1", WordUtils.initials("i am here 123")); + } + + // ----------------------------------------------------------------------- + public void testInitials_String_charArray() { + char[] array = null; + assertEquals(null, WordUtils.initials(null, array)); + assertEquals("", WordUtils.initials("", array)); + assertEquals("", WordUtils.initials(" ", array)); + assertEquals("I", WordUtils.initials("I", array)); + assertEquals("i", WordUtils.initials("i", array)); + assertEquals("S", WordUtils.initials("SJC", array)); + assertEquals("BJL", WordUtils.initials("Ben John Lee", array)); + assertEquals("BJ", WordUtils.initials("Ben J.Lee", array)); + assertEquals("BJ.L", WordUtils.initials(" Ben John . Lee", array)); + assertEquals("KO", WordUtils.initials("Kay O'Murphy", array)); + assertEquals("iah1", WordUtils.initials("i am here 123", array)); + + array = new char[0]; + assertEquals(null, WordUtils.initials(null, array)); + assertEquals("", WordUtils.initials("", array)); + assertEquals("", WordUtils.initials(" ", array)); + assertEquals("", WordUtils.initials("I", array)); + assertEquals("", WordUtils.initials("i", array)); + assertEquals("", WordUtils.initials("SJC", array)); + assertEquals("", WordUtils.initials("Ben John Lee", array)); + assertEquals("", WordUtils.initials("Ben J.Lee", array)); + assertEquals("", WordUtils.initials(" Ben John . Lee", array)); + assertEquals("", WordUtils.initials("Kay O'Murphy", array)); + assertEquals("", WordUtils.initials("i am here 123", array)); + + array = " ".toCharArray(); + assertEquals(null, WordUtils.initials(null, array)); + assertEquals("", WordUtils.initials("", array)); + assertEquals("", WordUtils.initials(" ", array)); + assertEquals("I", WordUtils.initials("I", array)); + assertEquals("i", WordUtils.initials("i", array)); + assertEquals("S", WordUtils.initials("SJC", array)); + assertEquals("BJL", WordUtils.initials("Ben John Lee", array)); + assertEquals("BJ", WordUtils.initials("Ben J.Lee", array)); + assertEquals("BJ.L", WordUtils.initials(" Ben John . Lee", array)); + assertEquals("KO", WordUtils.initials("Kay O'Murphy", array)); + assertEquals("iah1", WordUtils.initials("i am here 123", array)); + + array = " .".toCharArray(); + assertEquals(null, WordUtils.initials(null, array)); + assertEquals("", WordUtils.initials("", array)); + assertEquals("", WordUtils.initials(" ", array)); + assertEquals("I", WordUtils.initials("I", array)); + assertEquals("i", WordUtils.initials("i", array)); + assertEquals("S", WordUtils.initials("SJC", array)); + assertEquals("BJL", WordUtils.initials("Ben John Lee", array)); + assertEquals("BJL", WordUtils.initials("Ben J.Lee", array)); + assertEquals("BJL", WordUtils.initials(" Ben John . Lee", array)); + assertEquals("KO", WordUtils.initials("Kay O'Murphy", array)); + assertEquals("iah1", WordUtils.initials("i am here 123", array)); + + array = " .'".toCharArray(); + assertEquals(null, WordUtils.initials(null, array)); + assertEquals("", WordUtils.initials("", array)); + assertEquals("", WordUtils.initials(" ", array)); + assertEquals("I", WordUtils.initials("I", array)); + assertEquals("i", WordUtils.initials("i", array)); + assertEquals("S", WordUtils.initials("SJC", array)); + assertEquals("BJL", WordUtils.initials("Ben John Lee", array)); + assertEquals("BJL", WordUtils.initials("Ben J.Lee", array)); + assertEquals("BJL", WordUtils.initials(" Ben John . Lee", array)); + assertEquals("KOM", WordUtils.initials("Kay O'Murphy", array)); + assertEquals("iah1", WordUtils.initials("i am here 123", array)); + + array = "SIJo1".toCharArray(); + assertEquals(null, WordUtils.initials(null, array)); + assertEquals("", WordUtils.initials("", array)); + assertEquals(" ", WordUtils.initials(" ", array)); + assertEquals("", WordUtils.initials("I", array)); + assertEquals("i", WordUtils.initials("i", array)); + assertEquals("C", WordUtils.initials("SJC", array)); + assertEquals("Bh", WordUtils.initials("Ben John Lee", array)); + assertEquals("B.", WordUtils.initials("Ben J.Lee", array)); + assertEquals(" h", WordUtils.initials(" Ben John . Lee", array)); + assertEquals("K", WordUtils.initials("Kay O'Murphy", array)); + assertEquals("i2", WordUtils.initials("i am here 123", array)); + } + + // ----------------------------------------------------------------------- + public void testSwapCase_String() { + assertEquals(null, WordUtils.swapCase(null)); + assertEquals("", WordUtils.swapCase("")); + assertEquals(" ", WordUtils.swapCase(" ")); + + assertEquals("i", WordUtils.swapCase("I") ); + assertEquals("I", WordUtils.swapCase("i") ); + assertEquals("I AM HERE 123", WordUtils.swapCase("i am here 123") ); + assertEquals("i aM hERE 123", WordUtils.swapCase("I Am Here 123") ); + assertEquals("I AM here 123", WordUtils.swapCase("i am HERE 123") ); + assertEquals("i am here 123", WordUtils.swapCase("I AM HERE 123") ); + + String test = "This String contains a TitleCase character: \u01C8"; + String expect = "tHIS sTRING CONTAINS A tITLEcASE CHARACTER: \u01C9"; + assertEquals(expect, WordUtils.swapCase(test)); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/text/package.html b/ApacheCommonsLang/org/apache/commons/lang3/text/package.html new file mode 100644 index 0000000..d5f5cc5 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/text/package.html @@ -0,0 +1,26 @@ + + + +

    +Provides classes for handling and manipulating text, partly as an extension to {@link java.text}. +The classes in this package are, for the most part, intended to be instantiated. +(ie. they are not utility classes with lots of static methods) +

    +@since 2.1 + + diff --git a/ApacheCommonsLang/org/apache/commons/lang3/text/translate/AggregateTranslator.java b/ApacheCommonsLang/org/apache/commons/lang3/text/translate/AggregateTranslator.java new file mode 100644 index 0000000..309ebc0 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/text/translate/AggregateTranslator.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.text.translate; + +import java.io.IOException; +import java.io.Writer; + +import org.apache.commons.lang3.ArrayUtils; + +/** + * Executes a sequence of translators one after the other. Execution ends whenever + * the first translator consumes codepoints from the input. + * + * @since 3.0 + * @version $Id: AggregateTranslator.java 1088899 2011-04-05 05:31:27Z bayard $ + */ +public class AggregateTranslator extends CharSequenceTranslator { + + private final CharSequenceTranslator[] translators; + + /** + * Specify the translators to be used at creation time. + * + * @param translators CharSequenceTranslator array to aggregate + */ + public AggregateTranslator(CharSequenceTranslator... translators) { + this.translators = ArrayUtils.clone(translators); + } + + /** + * The first translator to consume codepoints from the input is the 'winner'. + * Execution stops with the number of consumed codepoints being returned. + * {@inheritDoc} + */ + @Override + public int translate(CharSequence input, int index, Writer out) throws IOException { + for (CharSequenceTranslator translator : translators) { + int consumed = translator.translate(input, index, out); + if(consumed != 0) { + return consumed; + } + } + return 0; + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/text/translate/CharSequenceTranslator.java b/ApacheCommonsLang/org/apache/commons/lang3/text/translate/CharSequenceTranslator.java new file mode 100644 index 0000000..48d9088 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/text/translate/CharSequenceTranslator.java @@ -0,0 +1,125 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.text.translate; + +import java.io.IOException; +import java.io.StringWriter; +import java.io.Writer; +import java.util.Locale; + +/** + * An API for translating text. + * Its core use is to escape and unescape text. Because escaping and unescaping + * is completely contextual, the API does not present two separate signatures. + * + * @since 3.0 + * @version $Id: CharSequenceTranslator.java 1146844 2011-07-14 18:49:51Z mbenson $ + */ +public abstract class CharSequenceTranslator { + + /** + * Translate a set of codepoints, represented by an int index into a CharSequence, + * into another set of codepoints. The number of codepoints consumed must be returned, + * and the only IOExceptions thrown must be from interacting with the Writer so that + * the top level API may reliable ignore StringWriter IOExceptions. + * + * @param input CharSequence that is being translated + * @param index int representing the current point of translation + * @param out Writer to translate the text to + * @return int count of codepoints consumed + * @throws IOException if and only if the Writer produces an IOException + */ + public abstract int translate(CharSequence input, int index, Writer out) throws IOException; + + /** + * Helper for non-Writer usage. + * @param input CharSequence to be translated + * @return String output of translation + */ + public final String translate(CharSequence input) { + if (input == null) { + return null; + } + try { + StringWriter writer = new StringWriter(input.length() * 2); + translate(input, writer); + return writer.toString(); + } catch (IOException ioe) { + // this should never ever happen while writing to a StringWriter + throw new RuntimeException(ioe); + } + } + + /** + * Translate an input onto a Writer. This is intentionally final as its algorithm is + * tightly coupled with the abstract method of this class. + * + * @param input CharSequence that is being translated + * @param out Writer to translate the text to + * @throws IOException if and only if the Writer produces an IOException + */ + public final void translate(CharSequence input, Writer out) throws IOException { + if (out == null) { + throw new IllegalArgumentException("The Writer must not be null"); + } + if (input == null) { + return; + } + int pos = 0; + int len = input.length(); + while (pos < len) { + int consumed = translate(input, pos, out); + if (consumed == 0) { + char[] c = Character.toChars(Character.codePointAt(input, pos)); + out.write(c); + pos+= c.length; + continue; + } +// // contract with translators is that they have to understand codepoints +// // and they just took care of a surrogate pair + for (int pt = 0; pt < consumed; pt++) { + pos += Character.charCount(Character.codePointAt(input, pos)); + } + } + } + + /** + * Helper method to create a merger of this translator with another set of + * translators. Useful in customizing the standard functionality. + * + * @param translators CharSequenceTranslator array of translators to merge with this one + * @return CharSequenceTranslator merging this translator with the others + */ + public final CharSequenceTranslator with(CharSequenceTranslator... translators) { + CharSequenceTranslator[] newArray = new CharSequenceTranslator[translators.length + 1]; + newArray[0] = this; + System.arraycopy(translators, 0, newArray, 1, translators.length); + return new AggregateTranslator(newArray); + } + + /** + *

    Returns an upper case hexadecimal String for the given + * character.

    + * + * @param codepoint The codepoint to convert. + * @return An upper case hexadecimal String + */ + public static String hex(int codepoint) { + return Integer.toHexString(codepoint).toUpperCase(Locale.ENGLISH); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/text/translate/CodePointTranslator.java b/ApacheCommonsLang/org/apache/commons/lang3/text/translate/CodePointTranslator.java new file mode 100644 index 0000000..9cf9f83 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/text/translate/CodePointTranslator.java @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.text.translate; + +import java.io.IOException; +import java.io.Writer; + +/** + * Helper subclass to CharSequenceTranslator to allow for translations that + * will replace up to one character at a time. + * + * @since 3.0 + * @version $Id: CodePointTranslator.java 1139924 2011-06-26 19:32:14Z mbenson $ + */ +public abstract class CodePointTranslator extends CharSequenceTranslator { + + /** + * Implementation of translate that maps onto the abstract translate(int, Writer) method. + * {@inheritDoc} + */ + @Override + public final int translate(CharSequence input, int index, Writer out) throws IOException { + int codepoint = Character.codePointAt(input, index); + boolean consumed = translate(codepoint, out); + if (consumed) { + return 1; + } else { + return 0; + } + } + + /** + * Translate the specified codepoint into another. + * + * @param codepoint int character input to translate + * @param out Writer to optionally push the translated output to + * @return boolean as to whether translation occurred or not + * @throws IOException if and only if the Writer produces an IOException + */ + public abstract boolean translate(int codepoint, Writer out) throws IOException; + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/text/translate/EntityArrays.java b/ApacheCommonsLang/org/apache/commons/lang3/text/translate/EntityArrays.java new file mode 100644 index 0000000..7a8c416 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/text/translate/EntityArrays.java @@ -0,0 +1,425 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.text.translate; + +/** + * Class holding various entity data for HTML and XML - generally for use with + * the LookupTranslator. + * All arrays are of length [*][2]. + * + * @since 3.0 + * @version $Id: EntityArrays.java 1088899 2011-04-05 05:31:27Z bayard $ + */ +public class EntityArrays { + + /** + * Mapping to escape ISO-8859-1 + * characters to their named HTML 3.x equivalents. + * @return the mapping table + */ + public static String[][] ISO8859_1_ESCAPE() { return ISO8859_1_ESCAPE.clone(); } + private static final String[][] ISO8859_1_ESCAPE = { + {"\u00A0", " "}, // non-breaking space + {"\u00A1", "¡"}, // inverted exclamation mark + {"\u00A2", "¢"}, // cent sign + {"\u00A3", "£"}, // pound sign + {"\u00A4", "¤"}, // currency sign + {"\u00A5", "¥"}, // yen sign = yuan sign + {"\u00A6", "¦"}, // broken bar = broken vertical bar + {"\u00A7", "§"}, // section sign + {"\u00A8", "¨"}, // diaeresis = spacing diaeresis + {"\u00A9", "©"}, // - copyright sign + {"\u00AA", "ª"}, // feminine ordinal indicator + {"\u00AB", "«"}, // left-pointing double angle quotation mark = left pointing guillemet + {"\u00AC", "¬"}, // not sign + {"\u00AD", "­"}, // soft hyphen = discretionary hyphen + {"\u00AE", "®"}, // - registered trademark sign + {"\u00AF", "¯"}, // macron = spacing macron = overline = APL overbar + {"\u00B0", "°"}, // degree sign + {"\u00B1", "±"}, // plus-minus sign = plus-or-minus sign + {"\u00B2", "²"}, // superscript two = superscript digit two = squared + {"\u00B3", "³"}, // superscript three = superscript digit three = cubed + {"\u00B4", "´"}, // acute accent = spacing acute + {"\u00B5", "µ"}, // micro sign + {"\u00B6", "¶"}, // pilcrow sign = paragraph sign + {"\u00B7", "·"}, // middle dot = Georgian comma = Greek middle dot + {"\u00B8", "¸"}, // cedilla = spacing cedilla + {"\u00B9", "¹"}, // superscript one = superscript digit one + {"\u00BA", "º"}, // masculine ordinal indicator + {"\u00BB", "»"}, // right-pointing double angle quotation mark = right pointing guillemet + {"\u00BC", "¼"}, // vulgar fraction one quarter = fraction one quarter + {"\u00BD", "½"}, // vulgar fraction one half = fraction one half + {"\u00BE", "¾"}, // vulgar fraction three quarters = fraction three quarters + {"\u00BF", "¿"}, // inverted question mark = turned question mark + {"\u00C0", "À"}, // - uppercase A, grave accent + {"\u00C1", "Á"}, // - uppercase A, acute accent + {"\u00C2", "Â"}, // - uppercase A, circumflex accent + {"\u00C3", "Ã"}, // - uppercase A, tilde + {"\u00C4", "Ä"}, // - uppercase A, umlaut + {"\u00C5", "Å"}, // - uppercase A, ring + {"\u00C6", "Æ"}, // - uppercase AE + {"\u00C7", "Ç"}, // - uppercase C, cedilla + {"\u00C8", "È"}, // - uppercase E, grave accent + {"\u00C9", "É"}, // - uppercase E, acute accent + {"\u00CA", "Ê"}, // - uppercase E, circumflex accent + {"\u00CB", "Ë"}, // - uppercase E, umlaut + {"\u00CC", "Ì"}, // - uppercase I, grave accent + {"\u00CD", "Í"}, // - uppercase I, acute accent + {"\u00CE", "Î"}, // - uppercase I, circumflex accent + {"\u00CF", "Ï"}, // - uppercase I, umlaut + {"\u00D0", "Ð"}, // - uppercase Eth, Icelandic + {"\u00D1", "Ñ"}, // - uppercase N, tilde + {"\u00D2", "Ò"}, // - uppercase O, grave accent + {"\u00D3", "Ó"}, // - uppercase O, acute accent + {"\u00D4", "Ô"}, // - uppercase O, circumflex accent + {"\u00D5", "Õ"}, // - uppercase O, tilde + {"\u00D6", "Ö"}, // - uppercase O, umlaut + {"\u00D7", "×"}, // multiplication sign + {"\u00D8", "Ø"}, // - uppercase O, slash + {"\u00D9", "Ù"}, // - uppercase U, grave accent + {"\u00DA", "Ú"}, // - uppercase U, acute accent + {"\u00DB", "Û"}, // - uppercase U, circumflex accent + {"\u00DC", "Ü"}, // - uppercase U, umlaut + {"\u00DD", "Ý"}, // - uppercase Y, acute accent + {"\u00DE", "Þ"}, // - uppercase THORN, Icelandic + {"\u00DF", "ß"}, // - lowercase sharps, German + {"\u00E0", "à"}, // - lowercase a, grave accent + {"\u00E1", "á"}, // - lowercase a, acute accent + {"\u00E2", "â"}, // - lowercase a, circumflex accent + {"\u00E3", "ã"}, // - lowercase a, tilde + {"\u00E4", "ä"}, // - lowercase a, umlaut + {"\u00E5", "å"}, // - lowercase a, ring + {"\u00E6", "æ"}, // - lowercase ae + {"\u00E7", "ç"}, // - lowercase c, cedilla + {"\u00E8", "è"}, // - lowercase e, grave accent + {"\u00E9", "é"}, // - lowercase e, acute accent + {"\u00EA", "ê"}, // - lowercase e, circumflex accent + {"\u00EB", "ë"}, // - lowercase e, umlaut + {"\u00EC", "ì"}, // - lowercase i, grave accent + {"\u00ED", "í"}, // - lowercase i, acute accent + {"\u00EE", "î"}, // - lowercase i, circumflex accent + {"\u00EF", "ï"}, // - lowercase i, umlaut + {"\u00F0", "ð"}, // - lowercase eth, Icelandic + {"\u00F1", "ñ"}, // - lowercase n, tilde + {"\u00F2", "ò"}, // - lowercase o, grave accent + {"\u00F3", "ó"}, // - lowercase o, acute accent + {"\u00F4", "ô"}, // - lowercase o, circumflex accent + {"\u00F5", "õ"}, // - lowercase o, tilde + {"\u00F6", "ö"}, // - lowercase o, umlaut + {"\u00F7", "÷"}, // division sign + {"\u00F8", "ø"}, // - lowercase o, slash + {"\u00F9", "ù"}, // - lowercase u, grave accent + {"\u00FA", "ú"}, // - lowercase u, acute accent + {"\u00FB", "û"}, // - lowercase u, circumflex accent + {"\u00FC", "ü"}, // - lowercase u, umlaut + {"\u00FD", "ý"}, // - lowercase y, acute accent + {"\u00FE", "þ"}, // - lowercase thorn, Icelandic + {"\u00FF", "ÿ"}, // - lowercase y, umlaut + }; + + /** + * Reverse of {@link #ISO8859_1_ESCAPE()} for unescaping purposes. + * @return the mapping table + */ + public static String[][] ISO8859_1_UNESCAPE() { return ISO8859_1_UNESCAPE.clone(); } + private static final String[][] ISO8859_1_UNESCAPE = invert(ISO8859_1_ESCAPE); + + /** + * Mapping to escape additional character entity + * references. Note that this must be used with {@link #ISO8859_1_ESCAPE()} to get the full list of + * HTML 4.0 character entities. + * @return the mapping table + */ + public static String[][] HTML40_EXTENDED_ESCAPE() { return HTML40_EXTENDED_ESCAPE.clone(); } + private static final String[][] HTML40_EXTENDED_ESCAPE = { + // + {"\u0192", "ƒ"}, // latin small f with hook = function= florin, U+0192 ISOtech --> + // + {"\u0391", "Α"}, // greek capital letter alpha, U+0391 --> + {"\u0392", "Β"}, // greek capital letter beta, U+0392 --> + {"\u0393", "Γ"}, // greek capital letter gamma,U+0393 ISOgrk3 --> + {"\u0394", "Δ"}, // greek capital letter delta,U+0394 ISOgrk3 --> + {"\u0395", "Ε"}, // greek capital letter epsilon, U+0395 --> + {"\u0396", "Ζ"}, // greek capital letter zeta, U+0396 --> + {"\u0397", "Η"}, // greek capital letter eta, U+0397 --> + {"\u0398", "Θ"}, // greek capital letter theta,U+0398 ISOgrk3 --> + {"\u0399", "Ι"}, // greek capital letter iota, U+0399 --> + {"\u039A", "Κ"}, // greek capital letter kappa, U+039A --> + {"\u039B", "Λ"}, // greek capital letter lambda,U+039B ISOgrk3 --> + {"\u039C", "Μ"}, // greek capital letter mu, U+039C --> + {"\u039D", "Ν"}, // greek capital letter nu, U+039D --> + {"\u039E", "Ξ"}, // greek capital letter xi, U+039E ISOgrk3 --> + {"\u039F", "Ο"}, // greek capital letter omicron, U+039F --> + {"\u03A0", "Π"}, // greek capital letter pi, U+03A0 ISOgrk3 --> + {"\u03A1", "Ρ"}, // greek capital letter rho, U+03A1 --> + // + {"\u03A3", "Σ"}, // greek capital letter sigma,U+03A3 ISOgrk3 --> + {"\u03A4", "Τ"}, // greek capital letter tau, U+03A4 --> + {"\u03A5", "Υ"}, // greek capital letter upsilon,U+03A5 ISOgrk3 --> + {"\u03A6", "Φ"}, // greek capital letter phi,U+03A6 ISOgrk3 --> + {"\u03A7", "Χ"}, // greek capital letter chi, U+03A7 --> + {"\u03A8", "Ψ"}, // greek capital letter psi,U+03A8 ISOgrk3 --> + {"\u03A9", "Ω"}, // greek capital letter omega,U+03A9 ISOgrk3 --> + {"\u03B1", "α"}, // greek small letter alpha,U+03B1 ISOgrk3 --> + {"\u03B2", "β"}, // greek small letter beta, U+03B2 ISOgrk3 --> + {"\u03B3", "γ"}, // greek small letter gamma,U+03B3 ISOgrk3 --> + {"\u03B4", "δ"}, // greek small letter delta,U+03B4 ISOgrk3 --> + {"\u03B5", "ε"}, // greek small letter epsilon,U+03B5 ISOgrk3 --> + {"\u03B6", "ζ"}, // greek small letter zeta, U+03B6 ISOgrk3 --> + {"\u03B7", "η"}, // greek small letter eta, U+03B7 ISOgrk3 --> + {"\u03B8", "θ"}, // greek small letter theta,U+03B8 ISOgrk3 --> + {"\u03B9", "ι"}, // greek small letter iota, U+03B9 ISOgrk3 --> + {"\u03BA", "κ"}, // greek small letter kappa,U+03BA ISOgrk3 --> + {"\u03BB", "λ"}, // greek small letter lambda,U+03BB ISOgrk3 --> + {"\u03BC", "μ"}, // greek small letter mu, U+03BC ISOgrk3 --> + {"\u03BD", "ν"}, // greek small letter nu, U+03BD ISOgrk3 --> + {"\u03BE", "ξ"}, // greek small letter xi, U+03BE ISOgrk3 --> + {"\u03BF", "ο"}, // greek small letter omicron, U+03BF NEW --> + {"\u03C0", "π"}, // greek small letter pi, U+03C0 ISOgrk3 --> + {"\u03C1", "ρ"}, // greek small letter rho, U+03C1 ISOgrk3 --> + {"\u03C2", "ς"}, // greek small letter final sigma,U+03C2 ISOgrk3 --> + {"\u03C3", "σ"}, // greek small letter sigma,U+03C3 ISOgrk3 --> + {"\u03C4", "τ"}, // greek small letter tau, U+03C4 ISOgrk3 --> + {"\u03C5", "υ"}, // greek small letter upsilon,U+03C5 ISOgrk3 --> + {"\u03C6", "φ"}, // greek small letter phi, U+03C6 ISOgrk3 --> + {"\u03C7", "χ"}, // greek small letter chi, U+03C7 ISOgrk3 --> + {"\u03C8", "ψ"}, // greek small letter psi, U+03C8 ISOgrk3 --> + {"\u03C9", "ω"}, // greek small letter omega,U+03C9 ISOgrk3 --> + {"\u03D1", "ϑ"}, // greek small letter theta symbol,U+03D1 NEW --> + {"\u03D2", "ϒ"}, // greek upsilon with hook symbol,U+03D2 NEW --> + {"\u03D6", "ϖ"}, // greek pi symbol, U+03D6 ISOgrk3 --> + // + {"\u2022", "•"}, // bullet = black small circle,U+2022 ISOpub --> + // + {"\u2026", "…"}, // horizontal ellipsis = three dot leader,U+2026 ISOpub --> + {"\u2032", "′"}, // prime = minutes = feet, U+2032 ISOtech --> + {"\u2033", "″"}, // double prime = seconds = inches,U+2033 ISOtech --> + {"\u203E", "‾"}, // overline = spacing overscore,U+203E NEW --> + {"\u2044", "⁄"}, // fraction slash, U+2044 NEW --> + // + {"\u2118", "℘"}, // script capital P = power set= Weierstrass p, U+2118 ISOamso --> + {"\u2111", "ℑ"}, // blackletter capital I = imaginary part,U+2111 ISOamso --> + {"\u211C", "ℜ"}, // blackletter capital R = real part symbol,U+211C ISOamso --> + {"\u2122", "™"}, // trade mark sign, U+2122 ISOnum --> + {"\u2135", "ℵ"}, // alef symbol = first transfinite cardinal,U+2135 NEW --> + // + // + {"\u2190", "←"}, // leftwards arrow, U+2190 ISOnum --> + {"\u2191", "↑"}, // upwards arrow, U+2191 ISOnum--> + {"\u2192", "→"}, // rightwards arrow, U+2192 ISOnum --> + {"\u2193", "↓"}, // downwards arrow, U+2193 ISOnum --> + {"\u2194", "↔"}, // left right arrow, U+2194 ISOamsa --> + {"\u21B5", "↵"}, // downwards arrow with corner leftwards= carriage return, U+21B5 NEW --> + {"\u21D0", "⇐"}, // leftwards double arrow, U+21D0 ISOtech --> + // + {"\u21D1", "⇑"}, // upwards double arrow, U+21D1 ISOamsa --> + {"\u21D2", "⇒"}, // rightwards double arrow,U+21D2 ISOtech --> + // + {"\u21D3", "⇓"}, // downwards double arrow, U+21D3 ISOamsa --> + {"\u21D4", "⇔"}, // left right double arrow,U+21D4 ISOamsa --> + // + {"\u2200", "∀"}, // for all, U+2200 ISOtech --> + {"\u2202", "∂"}, // partial differential, U+2202 ISOtech --> + {"\u2203", "∃"}, // there exists, U+2203 ISOtech --> + {"\u2205", "∅"}, // empty set = null set = diameter,U+2205 ISOamso --> + {"\u2207", "∇"}, // nabla = backward difference,U+2207 ISOtech --> + {"\u2208", "∈"}, // element of, U+2208 ISOtech --> + {"\u2209", "∉"}, // not an element of, U+2209 ISOtech --> + {"\u220B", "∋"}, // contains as member, U+220B ISOtech --> + // + {"\u220F", "∏"}, // n-ary product = product sign,U+220F ISOamsb --> + // + {"\u2211", "∑"}, // n-ary summation, U+2211 ISOamsb --> + // + {"\u2212", "−"}, // minus sign, U+2212 ISOtech --> + {"\u2217", "∗"}, // asterisk operator, U+2217 ISOtech --> + {"\u221A", "√"}, // square root = radical sign,U+221A ISOtech --> + {"\u221D", "∝"}, // proportional to, U+221D ISOtech --> + {"\u221E", "∞"}, // infinity, U+221E ISOtech --> + {"\u2220", "∠"}, // angle, U+2220 ISOamso --> + {"\u2227", "∧"}, // logical and = wedge, U+2227 ISOtech --> + {"\u2228", "∨"}, // logical or = vee, U+2228 ISOtech --> + {"\u2229", "∩"}, // intersection = cap, U+2229 ISOtech --> + {"\u222A", "∪"}, // union = cup, U+222A ISOtech --> + {"\u222B", "∫"}, // integral, U+222B ISOtech --> + {"\u2234", "∴"}, // therefore, U+2234 ISOtech --> + {"\u223C", "∼"}, // tilde operator = varies with = similar to,U+223C ISOtech --> + // + {"\u2245", "≅"}, // approximately equal to, U+2245 ISOtech --> + {"\u2248", "≈"}, // almost equal to = asymptotic to,U+2248 ISOamsr --> + {"\u2260", "≠"}, // not equal to, U+2260 ISOtech --> + {"\u2261", "≡"}, // identical to, U+2261 ISOtech --> + {"\u2264", "≤"}, // less-than or equal to, U+2264 ISOtech --> + {"\u2265", "≥"}, // greater-than or equal to,U+2265 ISOtech --> + {"\u2282", "⊂"}, // subset of, U+2282 ISOtech --> + {"\u2283", "⊃"}, // superset of, U+2283 ISOtech --> + // + {"\u2286", "⊆"}, // subset of or equal to, U+2286 ISOtech --> + {"\u2287", "⊇"}, // superset of or equal to,U+2287 ISOtech --> + {"\u2295", "⊕"}, // circled plus = direct sum,U+2295 ISOamsb --> + {"\u2297", "⊗"}, // circled times = vector product,U+2297 ISOamsb --> + {"\u22A5", "⊥"}, // up tack = orthogonal to = perpendicular,U+22A5 ISOtech --> + {"\u22C5", "⋅"}, // dot operator, U+22C5 ISOamsb --> + // + // + {"\u2308", "⌈"}, // left ceiling = apl upstile,U+2308 ISOamsc --> + {"\u2309", "⌉"}, // right ceiling, U+2309 ISOamsc --> + {"\u230A", "⌊"}, // left floor = apl downstile,U+230A ISOamsc --> + {"\u230B", "⌋"}, // right floor, U+230B ISOamsc --> + {"\u2329", "⟨"}, // left-pointing angle bracket = bra,U+2329 ISOtech --> + // + {"\u232A", "⟩"}, // right-pointing angle bracket = ket,U+232A ISOtech --> + // + // + {"\u25CA", "◊"}, // lozenge, U+25CA ISOpub --> + // + {"\u2660", "♠"}, // black spade suit, U+2660 ISOpub --> + // + {"\u2663", "♣"}, // black club suit = shamrock,U+2663 ISOpub --> + {"\u2665", "♥"}, // black heart suit = valentine,U+2665 ISOpub --> + {"\u2666", "♦"}, // black diamond suit, U+2666 ISOpub --> + + // + {"\u0152", "Œ"}, // -- latin capital ligature OE,U+0152 ISOlat2 --> + {"\u0153", "œ"}, // -- latin small ligature oe, U+0153 ISOlat2 --> + // + {"\u0160", "Š"}, // -- latin capital letter S with caron,U+0160 ISOlat2 --> + {"\u0161", "š"}, // -- latin small letter s with caron,U+0161 ISOlat2 --> + {"\u0178", "Ÿ"}, // -- latin capital letter Y with diaeresis,U+0178 ISOlat2 --> + // + {"\u02C6", "ˆ"}, // -- modifier letter circumflex accent,U+02C6 ISOpub --> + {"\u02DC", "˜"}, // small tilde, U+02DC ISOdia --> + // + {"\u2002", " "}, // en space, U+2002 ISOpub --> + {"\u2003", " "}, // em space, U+2003 ISOpub --> + {"\u2009", " "}, // thin space, U+2009 ISOpub --> + {"\u200C", "‌"}, // zero width non-joiner,U+200C NEW RFC 2070 --> + {"\u200D", "‍"}, // zero width joiner, U+200D NEW RFC 2070 --> + {"\u200E", "‎"}, // left-to-right mark, U+200E NEW RFC 2070 --> + {"\u200F", "‏"}, // right-to-left mark, U+200F NEW RFC 2070 --> + {"\u2013", "–"}, // en dash, U+2013 ISOpub --> + {"\u2014", "—"}, // em dash, U+2014 ISOpub --> + {"\u2018", "‘"}, // left single quotation mark,U+2018 ISOnum --> + {"\u2019", "’"}, // right single quotation mark,U+2019 ISOnum --> + {"\u201A", "‚"}, // single low-9 quotation mark, U+201A NEW --> + {"\u201C", "“"}, // left double quotation mark,U+201C ISOnum --> + {"\u201D", "”"}, // right double quotation mark,U+201D ISOnum --> + {"\u201E", "„"}, // double low-9 quotation mark, U+201E NEW --> + {"\u2020", "†"}, // dagger, U+2020 ISOpub --> + {"\u2021", "‡"}, // double dagger, U+2021 ISOpub --> + {"\u2030", "‰"}, // per mille sign, U+2030 ISOtech --> + {"\u2039", "‹"}, // single left-pointing angle quotation mark,U+2039 ISO proposed --> + // + {"\u203A", "›"}, // single right-pointing angle quotation mark,U+203A ISO proposed --> + // + {"\u20AC", "€"}, // -- euro sign, U+20AC NEW --> + }; + + /** + * Reverse of {@link #HTML40_EXTENDED_ESCAPE()} for unescaping purposes. + * @return the mapping table + */ + public static String[][] HTML40_EXTENDED_UNESCAPE() { return HTML40_EXTENDED_UNESCAPE.clone(); } + private static final String[][] HTML40_EXTENDED_UNESCAPE = invert(HTML40_EXTENDED_ESCAPE); + + /** + * Mapping to escape the basic XML and HTML character entities. + * + * Namely: {@code " & < >} + * @return the mapping table + */ + public static String[][] BASIC_ESCAPE() { return BASIC_ESCAPE.clone(); } + private static final String[][] BASIC_ESCAPE = { + {"\"", """}, // " - double-quote + {"&", "&"}, // & - ampersand + {"<", "<"}, // < - less-than + {">", ">"}, // > - greater-than + }; + + /** + * Reverse of {@link #BASIC_ESCAPE()} for unescaping purposes. + * @return the mapping table + */ + public static String[][] BASIC_UNESCAPE() { return BASIC_UNESCAPE.clone(); } + private static final String[][] BASIC_UNESCAPE = invert(BASIC_ESCAPE); + + /** + * Mapping to escape the apostrophe character to its XML character entity. + * @return the mapping table + */ + public static String[][] APOS_ESCAPE() { return APOS_ESCAPE.clone(); } + private static final String[][] APOS_ESCAPE = { + {"'", "'"}, // XML apostrophe + }; + + /** + * Reverse of {@link #APOS_ESCAPE()} for unescaping purposes. + * @return the mapping table + */ + public static String[][] APOS_UNESCAPE() { return APOS_UNESCAPE.clone(); } + private static final String[][] APOS_UNESCAPE = invert(APOS_ESCAPE); + + /** + * Mapping to escape the Java control characters. + * + * Namely: {@code \b \n \t \f \r} + * @return the mapping table + */ + public static String[][] JAVA_CTRL_CHARS_ESCAPE() { return JAVA_CTRL_CHARS_ESCAPE.clone(); } + private static final String[][] JAVA_CTRL_CHARS_ESCAPE = { + {"\b", "\\b"}, + {"\n", "\\n"}, + {"\t", "\\t"}, + {"\f", "\\f"}, + {"\r", "\\r"} + }; + + /** + * Reverse of {@link #JAVA_CTRL_CHARS_ESCAPE()} for unescaping purposes. + * @return the mapping table + */ + public static String[][] JAVA_CTRL_CHARS_UNESCAPE() { return JAVA_CTRL_CHARS_UNESCAPE.clone(); } + private static final String[][] JAVA_CTRL_CHARS_UNESCAPE = invert(JAVA_CTRL_CHARS_ESCAPE); + + /** + * Used to invert an escape array into an unescape array + * @param array String[][] to be inverted + * @return String[][] inverted array + */ + public static String[][] invert(String[][] array) { + String[][] newarray = new String[array.length][2]; + for(int i = 0; i col0 = new HashSet(); + Set col1 = new HashSet(); + String [][] sa = EntityArrays.HTML40_EXTENDED_ESCAPE(); + for(int i =0; i col0 = new HashSet(); + Set col1 = new HashSet(); + String [][] sa = EntityArrays.ISO8859_1_ESCAPE(); + boolean success = true; + for(int i =0; i lookupMap; + private final int shortest; + private final int longest; + + /** + * Define the lookup table to be used in translation + * + * @param lookup CharSequence[][] table of size [*][2] + */ + public LookupTranslator(CharSequence[]... lookup) { + lookupMap = new HashMap(); + int _shortest = Integer.MAX_VALUE; + int _longest = 0; + if (lookup != null) { + for (CharSequence[] seq : lookup) { + this.lookupMap.put(seq[0], seq[1]); + int sz = seq[0].length(); + if (sz < _shortest) { + _shortest = sz; + } + if (sz > _longest) { + _longest = sz; + } + } + } + shortest = _shortest; + longest = _longest; + } + + /** + * {@inheritDoc} + */ + @Override + public int translate(CharSequence input, int index, Writer out) throws IOException { + int max = longest; + if (index + longest > input.length()) { + max = input.length() - index; + } + // descend so as to get a greedy algorithm + for (int i = max; i >= shortest; i--) { + CharSequence subSeq = input.subSequence(index, index + i); + CharSequence result = lookupMap.get(subSeq); + if (result != null) { + out.write(result.toString()); + return i; + } + } + return 0; + } +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/text/translate/LookupTranslatorTest.java b/ApacheCommonsLang/org/apache/commons/lang3/text/translate/LookupTranslatorTest.java new file mode 100644 index 0000000..e1a1c5a --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/text/translate/LookupTranslatorTest.java @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.lang3.text.translate; + +import java.io.IOException; +import java.io.StringWriter; + +import junit.framework.TestCase; + +/** + * Unit tests for {@link org.apache.commons.lang3.text.translate.LookupTranslator}. + * @version $Id: LookupTranslatorTest.java 1022749 2010-10-14 22:49:55Z ggregory $ + */ +public class LookupTranslatorTest extends TestCase { + + public void testBasicLookup() throws IOException { + LookupTranslator lt = new LookupTranslator(new CharSequence[][] { { "one", "two" } }); + StringWriter out = new StringWriter(); + int result = lt.translate("one", 0, out); + assertEquals("Incorrect codepoint consumption", 3, result); + assertEquals("Incorrect value", "two", out.toString()); + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/text/translate/NumericEntityEscaper.java b/ApacheCommonsLang/org/apache/commons/lang3/text/translate/NumericEntityEscaper.java new file mode 100644 index 0000000..f99841a --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/text/translate/NumericEntityEscaper.java @@ -0,0 +1,119 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.text.translate; + +import java.io.IOException; +import java.io.Writer; + +/** + * Translates codepoints to their XML numeric entity escaped value. + * + * @since 3.0 + * @version $Id: NumericEntityEscaper.java 1142151 2011-07-02 04:06:23Z bayard $ + */ +public class NumericEntityEscaper extends CodePointTranslator { + + private final int below; + private final int above; + private final boolean between; + + /** + *

    Constructs a NumericEntityEscaper for the specified range. This is + * the underlying method for the other constructors/builders. The below + * and above boundaries are inclusive when between is + * true and exclusive when it is false.

    + * + * @param below int value representing the lowest codepoint boundary + * @param above int value representing the highest codepoint boundary + * @param between whether to escape between the boundaries or outside them + */ + private NumericEntityEscaper(int below, int above, boolean between) { + this.below = below; + this.above = above; + this.between = between; + } + + /** + *

    Constructs a NumericEntityEscaper for all characters.

    + */ + public NumericEntityEscaper() { + this(0, Integer.MAX_VALUE, true); + } + + /** + *

    Constructs a NumericEntityEscaper below the specified value (exclusive).

    + * + * @param codepoint below which to escape + * @return the newly created {@code NumericEntityEscaper} instance + */ + public static NumericEntityEscaper below(int codepoint) { + return outsideOf(codepoint, Integer.MAX_VALUE); + } + + /** + *

    Constructs a NumericEntityEscaper above the specified value (exclusive).

    + * + * @param codepoint above which to escape + * @return the newly created {@code NumericEntityEscaper} instance + */ + public static NumericEntityEscaper above(int codepoint) { + return outsideOf(0, codepoint); + } + + /** + *

    Constructs a NumericEntityEscaper between the specified values (inclusive).

    + * + * @param codepointLow above which to escape + * @param codepointHigh below which to escape + * @return the newly created {@code NumericEntityEscaper} instance + */ + public static NumericEntityEscaper between(int codepointLow, int codepointHigh) { + return new NumericEntityEscaper(codepointLow, codepointHigh, true); + } + + /** + *

    Constructs a NumericEntityEscaper outside of the specified values (exclusive).

    + * + * @param codepointLow below which to escape + * @param codepointHigh above which to escape + * @return the newly created {@code NumericEntityEscaper} instance + */ + public static NumericEntityEscaper outsideOf(int codepointLow, int codepointHigh) { + return new NumericEntityEscaper(codepointLow, codepointHigh, false); + } + + /** + * {@inheritDoc} + */ + @Override + public boolean translate(int codepoint, Writer out) throws IOException { + if(between) { + if (codepoint < below || codepoint > above) { + return false; + } + } else { + if (codepoint >= below && codepoint <= above) { + return false; + } + } + + out.write("&#"); + out.write(Integer.toString(codepoint, 10)); + out.write(';'); + return true; + } +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/text/translate/NumericEntityEscaperTest.java b/ApacheCommonsLang/org/apache/commons/lang3/text/translate/NumericEntityEscaperTest.java new file mode 100644 index 0000000..686f5d8 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/text/translate/NumericEntityEscaperTest.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.commons.lang3.text.translate; + +import junit.framework.TestCase; + +/** + * Unit tests for {@link org.apache.commons.lang3.text.translate.NumericEntityEscaper}. + * @version $Id: NumericEntityEscaperTest.java 1142151 2011-07-02 04:06:23Z bayard $ + */ +public class NumericEntityEscaperTest extends TestCase { + + public void testBelow() { + NumericEntityEscaper nee = NumericEntityEscaper.below('F'); + + String input = "ADFGZ"; + String result = nee.translate(input); + assertEquals("Failed to escape numeric entities via the below method", "ADFGZ", result); + } + + public void testBetween() { + NumericEntityEscaper nee = NumericEntityEscaper.between('F', 'L'); + + String input = "ADFGZ"; + String result = nee.translate(input); + assertEquals("Failed to escape numeric entities via the between method", "ADFGZ", result); + } + + public void testAbove() { + NumericEntityEscaper nee = NumericEntityEscaper.above('F'); + + String input = "ADFGZ"; + String result = nee.translate(input); + assertEquals("Failed to escape numeric entities via the above method", "ADFGZ", result); + } + + // See LANG-617 + public void testSupplementary() { + NumericEntityEscaper nee = new NumericEntityEscaper(); + String input = "\uD803\uDC22"; + String expected = "𐰢"; + + String result = nee.translate(input); + assertEquals("Failed to escape numeric entities supplementary characters", expected, result); + + } + +} diff --git a/ApacheCommonsLang/org/apache/commons/lang3/text/translate/NumericEntityUnescaper.java b/ApacheCommonsLang/org/apache/commons/lang3/text/translate/NumericEntityUnescaper.java new file mode 100644 index 0000000..ec206f4 --- /dev/null +++ b/ApacheCommonsLang/org/apache/commons/lang3/text/translate/NumericEntityUnescaper.java @@ -0,0 +1,139 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.commons.lang3.text.translate; + +import java.io.IOException; +import java.io.Writer; +import java.util.Arrays; +import java.util.EnumSet; + +/** + * Translate XML numeric entities of the form &#[xX]?\d+;? to + * the specific codepoint. + * + * Note that the semi-colon is optional. + * + * @since 3.0 + * @version $Id: NumericEntityUnescaper.java 1199894 2011-11-09 17:53:59Z ggregory $ + */ +public class NumericEntityUnescaper extends CharSequenceTranslator { + + public static enum OPTION { semiColonRequired, semiColonOptional, errorIfNoSemiColon } + + // TODO?: Create an OptionsSet class to hide some of the conditional logic below + private final EnumSet