From e08174337d35e2c6736b7accf2807d3b98defcff Mon Sep 17 00:00:00 2001 From: Dave Shepperton Date: Sat, 23 May 2026 15:12:27 -0700 Subject: [PATCH 01/22] Extensive Additions and Modifications Getting Ready for v4.0.0. --- pom.xml | 174 +- .../commons/codec/Base64Util.java | 266 +++ .../commons/codec/HexUtil.java | 95 + .../commons/codec/HtmlEncodingUtil.java | 439 ++++ .../commons/codec/JavaScriptEncodingUtil.java | 247 +++ .../commons/codec/MD5Util.java | 290 +++ .../commons/config/Configuration.java | 161 ++ .../commons/config/ConfigurationLocator.java | 66 + .../DynamicForwardingConfiguration.java | 51 + .../config/ForwardingConfiguration.java | 130 ++ .../commons/config/NamedConfiguration.java | 61 - .../PropertyNameMappingConfiguration.java | 100 + .../config/StaticForwardingConfiguration.java | 51 + .../commons/html/HtmlUtil.java | 741 +++++++ .../commons/image/AbstractIconFile.java | 210 ++ .../commons/image/ForwardingIcon.java | 161 ++ .../commons/image/ForwardingIconFile.java | 53 + .../commons/image/HEIFUtil.java | 45 + .../tractionsoftware/commons/image/Icon.java | 288 +++ .../commons/image/IconFileResource.java | 218 ++ .../commons/image/ImageUtil.java | 630 ++++++ .../SimpleFileResourceIconFileAdapter.java | 144 ++ .../commons/image/SimpleIcon.java | 137 ++ .../commons/image/WebPUtil.java | 415 ++++ .../commons/io/AbstractCustomPrintWriter.java | 142 ++ .../commons/io/ByteBufferInputStream.java | 109 + .../commons/io/FileIconService.java | 130 ++ .../commons/io/FileMetadata.java | 188 ++ .../io/FileMetadataBasedFileResource.java | 60 + .../commons/io/FileNameUtil.java | 767 +++++++ .../commons/io/FileResource.java | 673 ++++++ .../tractionsoftware/commons/io/FileUtil.java | 724 +++++++ .../commons/io/ForwardingFileResource.java | 103 + .../tractionsoftware/commons/io/IOUtil.java | 1218 +++++++++++ .../commons/io/JavaFileResource.java | 100 + .../commons/io/MutableFileMetadata.java | 193 ++ .../OutputStreamLimitExceededException.java | 31 + .../commons/io/SimpleMutableFileMetadata.java | 1124 ++++++++++ .../commons/io/SingleThreadPrintWriter.java | 268 +++ .../commons/io/SizedInputStream.java | 60 + .../commons/io/StringWriteUtil.java | 291 +++ .../tractionsoftware/commons/io/TempFile.java | 212 ++ .../commons/lang/EnhancedCharSequence.java | 1584 ++++++++++++++ .../commons/lang/EnumsUtil.java | 55 + .../commons/lang/JavaUtil.java | 229 ++ .../commons/lang/NativeTypeConversion.java | 626 ++++++ .../commons/{util => lang}/ObjectsUtil.java | 96 +- .../commons/lang/Resource.java | 140 ++ .../commons/lang/ResourceUtil.java | 249 +++ .../commons/lang/StringUtil.java | 1847 +++++++++++++++++ .../commons/{util => lang}/ThreadsUtil.java | 20 +- .../commons/mail/EmailAddress.java | 95 + .../commons/mail/EmailHeaders.java | 354 ++++ .../commons/mail/MailUtil.java | 1362 ++++++++++++ .../commons/net/HostAddressUtil.java | 642 ++++++ .../net/IPAddressNotAllowedException.java | 42 + .../commons/net/MediaTypeUtil.java | 334 +++ .../commons/net/URLBuilder.java | 35 + .../tractionsoftware/commons/net/URLUtil.java | 1464 +++++++++++++ .../net/http/server/HttpResultSender.java | 155 ++ .../commons/processor/Consumer.java | 48 + .../commons/processor/HasResultProvider.java | 33 + .../commons/processor/ProcessingUtil.java | 57 + .../commons/processor/Processor.java | 60 + .../commons/processor/Producer.java | 75 + .../commons/processor/Result.java | 205 ++ .../commons/processor/ResultProvider.java | 47 + .../commons/processor/TempFileResult.java | 294 +++ .../processor/TempFileResultProvider.java | 49 + .../AbstractCachingGetProperty.java | 80 + ...ava => AbstractCachingGetPutProperty.java} | 23 +- .../AbstractGetPropertyLocator.java | 139 ++ .../AbstractGetPropertyValueFilter.java | 54 + ...ava => AbstractGetPutPropertyLocator.java} | 25 +- .../properties/AbstractMapGetProperty.java | 119 ++ .../properties/AbstractMapPropertyLoader.java | 38 + .../properties/AbstractPropertyLoader.java | 47 + ...bstractPropertyNameMappingGetProperty.java | 82 + ...ractPropertyNameMappingGetPutProperty.java | 37 + ...PropertyNameMappingPropertyCollection.java | 51 + ...stractPropertyValueMappingGetProperty.java | 62 + ...actPropertyValueMappingGetPutProperty.java | 51 + ...ropertyValueMappingPropertyCollection.java | 73 + .../properties/BiFunctionPropertyLoader.java | 46 + .../properties/BiMapPropertyNameMapper.java | 57 + .../properties/CachingGetProperty.java | 52 + .../properties/CachingGetPutProperty.java | 54 + .../commons/properties/CachingPropStore.java | 60 + .../ChainedReadOnlyPropertyMap.java | 66 - .../commons/properties/CommitResult.java | 133 ++ .../commons/properties/CommitResults.java | 125 ++ .../commons/properties/ComplexProperty.java | 91 + .../DynamicForwardingGetProperty.java | 61 + .../DynamicForwardingGetPutProperty.java | 66 + .../DynamicForwardingPropStore.java | 62 + .../properties/ForwardingGetProperty.java | 200 ++ .../properties/ForwardingGetPutProperty.java | 125 ++ .../properties/ForwardingPropStore.java | 71 + .../ForwardingPropertyCollection.java | 94 + .../properties/ForwardingPutProperty.java | 285 +++ .../properties/GenericComplexProperty.java | 114 + .../commons/properties/GetProperties.java | 29 + .../commons/properties/GetProperty.java | 735 +++++++ .../commons/properties/GetPropertyAt.java | 60 + .../commons/properties/GetPropertyList.java | 37 + .../properties/GetPropertyLocator.java | 51 + .../GetPropertySpecificValuesFilter.java | 67 + .../commons/properties/GetPutProperty.java | 273 +++ .../GetPutPropertyChangeTracker.java | 206 ++ .../properties/GetPutPropertyLocator.java | 50 + .../properties/ImmutablePropStore.java | 192 ++ .../InvalidPropStoreChangesException.java | 88 + .../commons/properties/MapPropertyStore.java | 215 ++ .../commons/properties/ParseProperty.java | 72 - .../commons/properties/PropStore.java | 178 ++ .../PropStoreCommitErrorException.java | 88 + .../properties/PropStoreCommitException.java | 84 + .../commons/properties/PropStoreLocator.java | 70 + .../commons/properties/PropertiesRecord.java | 4 + .../commons/properties/PropertyAdapters.java | 197 ++ .../commons/properties/PropertyCache.java | 261 +++ .../properties/PropertyCollection.java | 147 ++ .../commons/properties/PropertyLoader.java | 57 + .../properties/PropertyNameMapper.java | 97 + .../PropertyNameMappingGetProperty.java | 68 + .../PropertyNameMappingGetPutProperty.java | 67 + .../PropertyNameMappingPropStore.java | 73 + .../PropertyValueMappingGetProperty.java | 62 + .../PropertyValueMappingGetPutProperty.java | 78 + .../PropertyValueMappingPropStore.java | 82 + .../PropertyValueMappingPutProperty.java | 71 + .../commons/properties/PutProperty.java | 337 +++ .../ReadOnlyPropStoreException.java | 89 + .../properties/ReadOnlyPropertyMap.java | 91 - .../SimpleCombinedPropertyNameMapper.java | 89 + ...Map.java => SimpleDynamicGetProperty.java} | 58 +- .../commons/properties/SimpleProperties.java | 1136 ++++++++++ .../properties/SimplePropertyNameMapper.java | 450 ++++ .../properties/SimplestGetPropertyAt.java | 113 + .../StaticForwardingGetProperty.java | 62 + .../StaticForwardingGetPutProperty.java | 67 + .../properties/StaticForwardingPropStore.java | 68 + .../StaticForwardingPropertyCollection.java | 54 + .../commons/properties/XMLGetProperty.java | 75 + .../text/CharBasedFilteringTextMapper.java | 308 +++ .../CodePointBasedFilteringTextMapper.java | 333 +++ .../commons/text/FilteringTextMapper.java | 437 ++++ .../commons/text/NumberFormats.java | 114 + .../commons/text/SnippetSize.java | 39 + .../commons/text/SnippetUtil.java | 458 ++++ .../commons/text/StringEscapeUtil.java | 304 +++ .../commons/text/StringSplitUtil.java | 390 ++++ .../commons/text/TextTokenizerService.java | 126 ++ .../text/TextTransformationException.java | 79 + .../commons/text/TextTransformer.java | 49 + .../commons/text/TextTransformerService.java | 56 + .../commons/text/TextWrapUtil.java | 356 ++++ .../util/AbstractLazyLoadingIterator.java | 91 + .../commons/util/ArraysUtil.java | 5 +- .../commons/util/CollectionsUtil.java | 365 +++- .../commons/util/ComparatorsUtil.java | 13 +- .../commons/util/CompressionUtil.java | 522 +++++ .../commons/util/DateFormats.java | 469 +++++ .../commons/util/DateType.java | 65 + .../commons/util/DateUtil.java | 377 ++++ .../util/DefaultFormatterProvider.java | 58 + .../commons/util/Dimensions.java | 406 ++++ .../commons/util/FormatterProvider.java | 32 + .../util/ForwardingSequencedCollection.java | 50 + .../commons/util/ForwardingSequencedSet.java | 63 + .../commons/util/LocaleUtil.java | 67 + .../commons/util/NativeTypeConversion.java | 208 -- .../commons/util/OneTimeRunnable.java | 2 +- .../commons/util/SimpleDurationUnit.java | 175 ++ .../util/SimplestUTCCalendarDateRange.java | 117 ++ .../commons/util/SimplestUTCDisplayDate.java | 121 ++ .../commons/util/TimeZoneUtil.java | 72 + .../commons/util/UTCCalendarDateRange.java | 198 ++ .../commons/util/UTCDisplayDate.java | 133 ++ .../function/CacheSupportingFunction.java | 2 +- .../function/CacheSupportingPredicate.java | 2 +- .../function/CacheSupportingSupplier.java | 2 +- .../commons/util/function/ConsumersUtil.java | 5 +- .../commons/util/function/FunctionsUtil.java | 2 +- .../commons/util/function/PredicatesUtil.java | 2 +- .../commons/util/function/SuppliersUtil.java | 2 +- .../xml/JaxpDocumentCreationException.java | 99 + .../commons/xml/JaxpException.java | 114 + .../xml/JaxpTransformerCreationException.java | 116 ++ .../commons/xml/JaxpUtil.java | 915 ++++++++ .../tractionsoftware/commons/xml/XmlUtil.java | 916 ++++++++ src/main/resources/META-INF/MANIFEST.MF | 2 +- 192 files changed, 38944 insertions(+), 747 deletions(-) create mode 100644 src/main/java/com/tractionsoftware/commons/codec/Base64Util.java create mode 100644 src/main/java/com/tractionsoftware/commons/codec/HexUtil.java create mode 100644 src/main/java/com/tractionsoftware/commons/codec/HtmlEncodingUtil.java create mode 100644 src/main/java/com/tractionsoftware/commons/codec/JavaScriptEncodingUtil.java create mode 100644 src/main/java/com/tractionsoftware/commons/codec/MD5Util.java create mode 100644 src/main/java/com/tractionsoftware/commons/config/Configuration.java create mode 100644 src/main/java/com/tractionsoftware/commons/config/ConfigurationLocator.java create mode 100644 src/main/java/com/tractionsoftware/commons/config/DynamicForwardingConfiguration.java create mode 100644 src/main/java/com/tractionsoftware/commons/config/ForwardingConfiguration.java delete mode 100644 src/main/java/com/tractionsoftware/commons/config/NamedConfiguration.java create mode 100644 src/main/java/com/tractionsoftware/commons/config/PropertyNameMappingConfiguration.java create mode 100644 src/main/java/com/tractionsoftware/commons/config/StaticForwardingConfiguration.java create mode 100644 src/main/java/com/tractionsoftware/commons/html/HtmlUtil.java create mode 100644 src/main/java/com/tractionsoftware/commons/image/AbstractIconFile.java create mode 100644 src/main/java/com/tractionsoftware/commons/image/ForwardingIcon.java create mode 100644 src/main/java/com/tractionsoftware/commons/image/ForwardingIconFile.java create mode 100644 src/main/java/com/tractionsoftware/commons/image/HEIFUtil.java create mode 100644 src/main/java/com/tractionsoftware/commons/image/Icon.java create mode 100644 src/main/java/com/tractionsoftware/commons/image/IconFileResource.java create mode 100644 src/main/java/com/tractionsoftware/commons/image/ImageUtil.java create mode 100644 src/main/java/com/tractionsoftware/commons/image/SimpleFileResourceIconFileAdapter.java create mode 100644 src/main/java/com/tractionsoftware/commons/image/SimpleIcon.java create mode 100644 src/main/java/com/tractionsoftware/commons/image/WebPUtil.java create mode 100644 src/main/java/com/tractionsoftware/commons/io/AbstractCustomPrintWriter.java create mode 100644 src/main/java/com/tractionsoftware/commons/io/ByteBufferInputStream.java create mode 100644 src/main/java/com/tractionsoftware/commons/io/FileIconService.java create mode 100644 src/main/java/com/tractionsoftware/commons/io/FileMetadata.java create mode 100644 src/main/java/com/tractionsoftware/commons/io/FileMetadataBasedFileResource.java create mode 100644 src/main/java/com/tractionsoftware/commons/io/FileNameUtil.java create mode 100644 src/main/java/com/tractionsoftware/commons/io/FileResource.java create mode 100644 src/main/java/com/tractionsoftware/commons/io/FileUtil.java create mode 100644 src/main/java/com/tractionsoftware/commons/io/ForwardingFileResource.java create mode 100644 src/main/java/com/tractionsoftware/commons/io/IOUtil.java create mode 100644 src/main/java/com/tractionsoftware/commons/io/JavaFileResource.java create mode 100644 src/main/java/com/tractionsoftware/commons/io/MutableFileMetadata.java create mode 100644 src/main/java/com/tractionsoftware/commons/io/OutputStreamLimitExceededException.java create mode 100644 src/main/java/com/tractionsoftware/commons/io/SimpleMutableFileMetadata.java create mode 100644 src/main/java/com/tractionsoftware/commons/io/SingleThreadPrintWriter.java create mode 100644 src/main/java/com/tractionsoftware/commons/io/SizedInputStream.java create mode 100644 src/main/java/com/tractionsoftware/commons/io/StringWriteUtil.java create mode 100644 src/main/java/com/tractionsoftware/commons/io/TempFile.java create mode 100644 src/main/java/com/tractionsoftware/commons/lang/EnhancedCharSequence.java create mode 100644 src/main/java/com/tractionsoftware/commons/lang/EnumsUtil.java create mode 100644 src/main/java/com/tractionsoftware/commons/lang/JavaUtil.java create mode 100644 src/main/java/com/tractionsoftware/commons/lang/NativeTypeConversion.java rename src/main/java/com/tractionsoftware/commons/{util => lang}/ObjectsUtil.java (65%) create mode 100644 src/main/java/com/tractionsoftware/commons/lang/Resource.java create mode 100644 src/main/java/com/tractionsoftware/commons/lang/ResourceUtil.java create mode 100644 src/main/java/com/tractionsoftware/commons/lang/StringUtil.java rename src/main/java/com/tractionsoftware/commons/{util => lang}/ThreadsUtil.java (90%) create mode 100644 src/main/java/com/tractionsoftware/commons/mail/EmailAddress.java create mode 100644 src/main/java/com/tractionsoftware/commons/mail/EmailHeaders.java create mode 100644 src/main/java/com/tractionsoftware/commons/mail/MailUtil.java create mode 100644 src/main/java/com/tractionsoftware/commons/net/HostAddressUtil.java create mode 100644 src/main/java/com/tractionsoftware/commons/net/IPAddressNotAllowedException.java create mode 100644 src/main/java/com/tractionsoftware/commons/net/MediaTypeUtil.java create mode 100644 src/main/java/com/tractionsoftware/commons/net/URLBuilder.java create mode 100644 src/main/java/com/tractionsoftware/commons/net/URLUtil.java create mode 100644 src/main/java/com/tractionsoftware/commons/net/http/server/HttpResultSender.java create mode 100644 src/main/java/com/tractionsoftware/commons/processor/Consumer.java create mode 100644 src/main/java/com/tractionsoftware/commons/processor/HasResultProvider.java create mode 100644 src/main/java/com/tractionsoftware/commons/processor/ProcessingUtil.java create mode 100644 src/main/java/com/tractionsoftware/commons/processor/Processor.java create mode 100644 src/main/java/com/tractionsoftware/commons/processor/Producer.java create mode 100644 src/main/java/com/tractionsoftware/commons/processor/Result.java create mode 100644 src/main/java/com/tractionsoftware/commons/processor/ResultProvider.java create mode 100644 src/main/java/com/tractionsoftware/commons/processor/TempFileResult.java create mode 100644 src/main/java/com/tractionsoftware/commons/processor/TempFileResultProvider.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/AbstractCachingGetProperty.java rename src/main/java/com/tractionsoftware/commons/properties/{FunctionReadOnlyPropertyMap.java => AbstractCachingGetPutProperty.java} (57%) create mode 100644 src/main/java/com/tractionsoftware/commons/properties/AbstractGetPropertyLocator.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/AbstractGetPropertyValueFilter.java rename src/main/java/com/tractionsoftware/commons/properties/{ForwardingReadOnlyPropertyMap.java => AbstractGetPutPropertyLocator.java} (55%) create mode 100644 src/main/java/com/tractionsoftware/commons/properties/AbstractMapGetProperty.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/AbstractMapPropertyLoader.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/AbstractPropertyLoader.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/AbstractPropertyNameMappingGetProperty.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/AbstractPropertyNameMappingGetPutProperty.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/AbstractPropertyNameMappingPropertyCollection.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/AbstractPropertyValueMappingGetProperty.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/AbstractPropertyValueMappingGetPutProperty.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/AbstractPropertyValueMappingPropertyCollection.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/BiFunctionPropertyLoader.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/BiMapPropertyNameMapper.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/CachingGetProperty.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/CachingGetPutProperty.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/CachingPropStore.java delete mode 100644 src/main/java/com/tractionsoftware/commons/properties/ChainedReadOnlyPropertyMap.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/CommitResult.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/CommitResults.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/ComplexProperty.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/DynamicForwardingGetProperty.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/DynamicForwardingGetPutProperty.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/DynamicForwardingPropStore.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/ForwardingGetProperty.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/ForwardingGetPutProperty.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/ForwardingPropStore.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/ForwardingPropertyCollection.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/ForwardingPutProperty.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/GenericComplexProperty.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/GetProperties.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/GetProperty.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/GetPropertyAt.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/GetPropertyList.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/GetPropertyLocator.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/GetPropertySpecificValuesFilter.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/GetPutProperty.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/GetPutPropertyChangeTracker.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/GetPutPropertyLocator.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/ImmutablePropStore.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/InvalidPropStoreChangesException.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/MapPropertyStore.java delete mode 100644 src/main/java/com/tractionsoftware/commons/properties/ParseProperty.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/PropStore.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/PropStoreCommitErrorException.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/PropStoreCommitException.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/PropStoreLocator.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/PropertiesRecord.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/PropertyAdapters.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/PropertyCache.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/PropertyCollection.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/PropertyLoader.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/PropertyNameMapper.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/PropertyNameMappingGetProperty.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/PropertyNameMappingGetPutProperty.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/PropertyNameMappingPropStore.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/PropertyValueMappingGetProperty.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/PropertyValueMappingGetPutProperty.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/PropertyValueMappingPropStore.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/PropertyValueMappingPutProperty.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/PutProperty.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/ReadOnlyPropStoreException.java delete mode 100644 src/main/java/com/tractionsoftware/commons/properties/ReadOnlyPropertyMap.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/SimpleCombinedPropertyNameMapper.java rename src/main/java/com/tractionsoftware/commons/properties/{SimpleDynamicReadOnlyPropertyMap.java => SimpleDynamicGetProperty.java} (64%) create mode 100644 src/main/java/com/tractionsoftware/commons/properties/SimpleProperties.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/SimplePropertyNameMapper.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/SimplestGetPropertyAt.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/StaticForwardingGetProperty.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/StaticForwardingGetPutProperty.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/StaticForwardingPropStore.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/StaticForwardingPropertyCollection.java create mode 100644 src/main/java/com/tractionsoftware/commons/properties/XMLGetProperty.java create mode 100644 src/main/java/com/tractionsoftware/commons/text/CharBasedFilteringTextMapper.java create mode 100644 src/main/java/com/tractionsoftware/commons/text/CodePointBasedFilteringTextMapper.java create mode 100644 src/main/java/com/tractionsoftware/commons/text/FilteringTextMapper.java create mode 100644 src/main/java/com/tractionsoftware/commons/text/NumberFormats.java create mode 100644 src/main/java/com/tractionsoftware/commons/text/SnippetSize.java create mode 100644 src/main/java/com/tractionsoftware/commons/text/SnippetUtil.java create mode 100644 src/main/java/com/tractionsoftware/commons/text/StringEscapeUtil.java create mode 100644 src/main/java/com/tractionsoftware/commons/text/StringSplitUtil.java create mode 100644 src/main/java/com/tractionsoftware/commons/text/TextTokenizerService.java create mode 100644 src/main/java/com/tractionsoftware/commons/text/TextTransformationException.java create mode 100644 src/main/java/com/tractionsoftware/commons/text/TextTransformer.java create mode 100644 src/main/java/com/tractionsoftware/commons/text/TextTransformerService.java create mode 100644 src/main/java/com/tractionsoftware/commons/text/TextWrapUtil.java create mode 100644 src/main/java/com/tractionsoftware/commons/util/AbstractLazyLoadingIterator.java create mode 100644 src/main/java/com/tractionsoftware/commons/util/CompressionUtil.java create mode 100644 src/main/java/com/tractionsoftware/commons/util/DateFormats.java create mode 100644 src/main/java/com/tractionsoftware/commons/util/DateType.java create mode 100644 src/main/java/com/tractionsoftware/commons/util/DateUtil.java create mode 100644 src/main/java/com/tractionsoftware/commons/util/DefaultFormatterProvider.java create mode 100644 src/main/java/com/tractionsoftware/commons/util/Dimensions.java create mode 100644 src/main/java/com/tractionsoftware/commons/util/FormatterProvider.java create mode 100644 src/main/java/com/tractionsoftware/commons/util/ForwardingSequencedCollection.java create mode 100644 src/main/java/com/tractionsoftware/commons/util/ForwardingSequencedSet.java create mode 100644 src/main/java/com/tractionsoftware/commons/util/LocaleUtil.java delete mode 100644 src/main/java/com/tractionsoftware/commons/util/NativeTypeConversion.java create mode 100644 src/main/java/com/tractionsoftware/commons/util/SimpleDurationUnit.java create mode 100644 src/main/java/com/tractionsoftware/commons/util/SimplestUTCCalendarDateRange.java create mode 100644 src/main/java/com/tractionsoftware/commons/util/SimplestUTCDisplayDate.java create mode 100644 src/main/java/com/tractionsoftware/commons/util/TimeZoneUtil.java create mode 100644 src/main/java/com/tractionsoftware/commons/util/UTCCalendarDateRange.java create mode 100644 src/main/java/com/tractionsoftware/commons/util/UTCDisplayDate.java create mode 100644 src/main/java/com/tractionsoftware/commons/xml/JaxpDocumentCreationException.java create mode 100644 src/main/java/com/tractionsoftware/commons/xml/JaxpException.java create mode 100644 src/main/java/com/tractionsoftware/commons/xml/JaxpTransformerCreationException.java create mode 100644 src/main/java/com/tractionsoftware/commons/xml/JaxpUtil.java create mode 100644 src/main/java/com/tractionsoftware/commons/xml/XmlUtil.java diff --git a/pom.xml b/pom.xml index 74c6e1f..1d46817 100644 --- a/pom.xml +++ b/pom.xml @@ -3,63 +3,131 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> - 4.0.0 + 4.0.0 - com.tractionsoftware.commons - tractionsoftware-commons - 3.1 - Traction Software Commons - 2016 - - A collection of general helper classes for Java. - - https://tractionsoftware.com/ + com.tractionsoftware.commons + tractionsoftware-commons + 4.0.0-SNAPSHOT + Traction Software Commons + 2016 + + A collection of general helper classes for Java. + + https://tractionsoftware.com/ - - - Dave Shepperton - shep - shep@tractionsoftware.com - Traction Software, Inc. - - Java Developer - - - + + + Dave Shepperton + shep + shep@tractionsoftware.com + Traction Software, Inc. + + Java Developer + + + - - - com.google.guava - guava - 33.4.8-jre - - - org.apache.commons - commons-lang3 - 3.18.0 - - + + + com.google.guava + guava + 33.6.0-jre + + + org.apache.commons + commons-lang3 + 3.20.0 + + + org.apache.commons + commons-text + 1.15.0 + + + commons-io + commons-io + 2.22.0 + + + commons-codec + commons-codec + 1.22.0 + + + jakarta.annotation + jakarta.annotation-api + 3.0.0 + + + jakarta.activation + jakarta.activation-api + 2.1.4 + + + jakarta.mail + jakarta.mail-api + 2.1.5 + compile + + + org.threeten + threeten-extra + 1.8.0 + compile + - - 21 - 21 - UTF-8 - UTF-8 - + + + com.tractionsoftware.heif-reader + heif-reader + 1.0.1 + system + ${project.basedir}/lib/heif-reader-1.0.1.jar + + + com.tractionsoftware.httpclient-wrappers + tractionsoftware-httpclient-wrappers-api + 1.0.1 + system + ${project.basedir}/lib/tractionsoftware-httpclient-wrappers-api-1.0.1.jar + + + com.tractionsoftware.httpclient-wrappers + tractionsoftware-httpclient-wrappers-apache-hc + 1.0.1 + system + ${project.basedir}/lib/tractionsoftware-httpclient-wrappers-apache-hc-1.0.1.jar + - - - - org.apache.maven.plugins - maven-jar-plugin - 3.4.2 - - - src/main/resources/META-INF/MANIFEST.MF - - - - - + + + org.mp4parser + isoparser + 1.9.56 + + + + + + 26 + 26 + UTF-8 + UTF-8 + + + + + + org.apache.maven.plugins + maven-jar-plugin + 3.5.0 + + + src/main/resources/META-INF/MANIFEST.MF + + + + + diff --git a/src/main/java/com/tractionsoftware/commons/codec/Base64Util.java b/src/main/java/com/tractionsoftware/commons/codec/Base64Util.java new file mode 100644 index 0000000..a953e10 --- /dev/null +++ b/src/main/java/com/tractionsoftware/commons/codec/Base64Util.java @@ -0,0 +1,266 @@ +/* + * + * Copyright 1996-2026 Traction Software, Inc. + * + * Licensed 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. + * + */ + +// PLEASE DO NOT DELETE THIS LINE - make copyright depends on it. + +package com.tractionsoftware.commons.codec; + +import com.tractionsoftware.commons.io.IOUtil; +import com.tractionsoftware.commons.io.SingleThreadPrintWriter; +import com.tractionsoftware.commons.text.SnippetUtil; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.*; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.util.Base64; + +public final class Base64Util { + + private static final Logger LOGGER = LoggerFactory.getLogger(Base64Util.class); + + private Base64Util() { + } + + public static Base64.Decoder getDecoder(boolean mime) { + if (mime) { + return Base64.getMimeDecoder(); + } + return Base64.getDecoder(); + } + + public static Base64.Decoder getMimeDecoder() { + return getDecoder(true); + } + + /** + * Decodes the base-64 data encoded in the given input String. + * + * @param encodedStr + * the String containing the base-64 encoded representation of the bytes. + * @return the bytes encoded in the base-64 encoding input String. + */ + public static byte[] getDecodedBytes(String encodedStr) { + return getDecodedBytes(encodedStr, false); + } + + public static byte[] getDecodedBytes(String encodedStr, boolean mime) { + if (encodedStr == null) { + return null; + } + try { + return getDecoder(mime).decode(encodedStr.getBytes()); + } + catch (RuntimeException e) { + LOGGER.warn( + "Failed to decode base 64 string value {}", SnippetUtil.truncatedToString(encodedStr, 100), e + ); + return ArrayUtils.EMPTY_BYTE_ARRAY; + } + } + + public static byte[] getDecodedBytes(byte[] encoding) { + return getDecodedBytes(encoding, false); + } + + public static byte[] getDecodedBytes(byte[] encoding, boolean mime) { + if (encoding == null) { + return null; + } + try { + return getDecoder(mime).decode(encoding); + } + catch (RuntimeException e) { + LOGGER.warn("Failed to decode base 64 bytes", e); + return ArrayUtils.EMPTY_BYTE_ARRAY; + } + } + + public static String getDecodedString(byte[] encoding) { + return getDecodedString(encoding, StandardCharsets.UTF_8, false); + } + + public static String getDecodedString(byte[] encoding, Charset charset, boolean mime) { + byte[] bytes = getDecodedBytes(encoding, mime); + if (bytes == null) { + return null; + } + return new String(bytes, charset); + } + + /** + * Creates a String from the UTF-8 encoded bytes represented by the given base-64 encoded String. + * + * @param encodedStr + * the String containing the base-64 encoded representation of the bytes. + * @return a String from the UTF-8 encoded bytes represented by the given base-64 encoded String. + */ + public static String getDecodedString(String encodedStr) { + return getDecodedString(encodedStr, StandardCharsets.UTF_8, false); + } + + public static String getDecodedString(String encodedStr, Charset charset, boolean mime) { + if (encodedStr == null) { + return null; + } + return new String(getDecodedBytes(encodedStr, mime), charset); + } + + /** + * Decodes the given String from Base64 and + * + * @param encodedStr + * representing the Base64 encoding. + * @param charset + * the Charset that should be used to interpret the Base64 bytes. + * @param mime + * whether mime decoding should be used. + * @param out + * to which the decoded String should be written. + * @throws IOException if there is an error writing to the Writer. + */ + public static void printDecodedString(String encodedStr, Charset charset, boolean mime, Writer out) + throws IOException { + if (StringUtils.isEmpty(encodedStr)) { + return; + } + try (Reader reader = getDecodingReader(encodedStr, charset, mime)) { + reader.transferTo(out); + } + } + + public static Base64.Encoder getEncoder(int bytesPerLine) { + if (bytesPerLine <= 0) { + return Base64.getEncoder(); + } + return Base64.getMimeEncoder(bytesPerLine, new byte[] { '\n' }); + } + + public static Base64.Encoder getDefaultMimeEncoder() { + return getEncoder(80); + } + + /** + * Creates a base-64 encoded String representation of the input byte array using the default number of bytes per + * line. + * + * @param bytes + * the bytes to be encoded. + * @return the base-64 encoded String representation of the input byte array using the default number of bytes per + * line. + */ + public static byte[] getEncodedBytes(byte[] bytes) { + if (bytes == null) { + return null; + } + return Base64.getEncoder().encode(bytes); + } + + public static byte[] getEncodedBytes(byte[] bytes, int bytesPerLine) { + if (bytes == null) { + return null; + } + return getEncoder(bytesPerLine).encode(bytes); + } + + public static String getEncodedString(byte[] bytes) { + if (bytes == null) { + return null; + } + return Base64.getEncoder().encodeToString(bytes); + } + + /** + * Creates a base-64 encoded String representation of the input byte array using the requested number of bytes per + * line. + * + * @param bytes + * the bytes to be encoded. + * @param bytesPerLine + * the requested number of bytes per line in the output String. After the given number of bytes, a CR/LF + * sequence will be inserted. Pass Integer.MAX_VALUE to omit CR/LF sequences entirely. The requested number of + * bytes per line may be rounded (e.g., to the nearest multiple of 4). + * @return the base-64 encoded String representation of the input byte array using requested number of bytes per + * line. + */ + public static String getEncodedString(byte[] bytes, int bytesPerLine) { + if (bytes == null) { + return null; + } + return getEncoder(bytesPerLine).encodeToString(bytes); + } + + /** + * Creates a base-64 encoded String representation of the bytes in the given input String as encoded in the UTF-8 + * character set. + * + * @param input + * the input String whose UTF-8 bytes are to be encoded in the output String. + * @return a base-64 encoded String representation of the bytes in the given input String as encoded in the UTF-8 + * character set. + */ + public static String getEncodedString(String input) { + return getEncodedString(input, -1); + } + + public static String getEncodedString(String input, int bytesPerLine) { + if (input == null) { + return null; + } + return getEncodedString(input.getBytes(StandardCharsets.UTF_8), bytesPerLine); + } + + public static void printEncodedString(String str, Charset charset, int bytesPerLine, Writer out) { + if (str != null) { + printEncodedString(str.getBytes(charset), bytesPerLine, out); + } + } + + public static void printEncodedString(byte[] strBytes, int bytesPerLine, Writer out) { + if (ArrayUtils.isEmpty(strBytes)) { + return; + } + try (OutputStream outStream = getEncodingOutputStream(bytesPerLine, SingleThreadPrintWriter.createInstance(out))) { + outStream.write(strBytes); + } + catch (IOException e) { + LOGGER.warn("Failed to encode base 64 bytes", e); + } + } + + private static OutputStream getEncodingOutputStream(int bytesPerLine, PrintWriter out) throws IOException { + return getEncoder(bytesPerLine).wrap( + IOUtil.getPrintWriterOutputStream(out, false) + ); + } + + private static InputStream getDecodingInputStream(String encodedStr, Charset charset, boolean mime) + throws IOException { + return getDecoder(mime).wrap(IOUtil.getStringAsInputStream(encodedStr, charset)); + } + + private static Reader getDecodingReader(String encodedStr, Charset charset, boolean mime) throws IOException { + return new InputStreamReader( + getDecodingInputStream(encodedStr, charset, mime), charset + ); + } + +} diff --git a/src/main/java/com/tractionsoftware/commons/codec/HexUtil.java b/src/main/java/com/tractionsoftware/commons/codec/HexUtil.java new file mode 100644 index 0000000..e7fdc2b --- /dev/null +++ b/src/main/java/com/tractionsoftware/commons/codec/HexUtil.java @@ -0,0 +1,95 @@ +/* + * + * Copyright 1996-2026 Traction Software, Inc. + * + * Licensed 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. + * + */ + +// PLEASE DO NOT DELETE THIS LINE - make copyright depends on it. + +package com.tractionsoftware.commons.codec; + +public final class HexUtil { + + /* + * Not instantiable. + */ + private HexUtil() { + } + + private static final char[] HEX_DIGITS = new char[] { + '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' + }; + + /** + * Returns a String representing a hex encoding of the bytes. Each byte is encoded with 2 hex chars. + * + * @param bytes + * the bytes from which the hex encoding is to be produced. + */ + public static String getEncodedString(byte[] bytes) { + + if (bytes == null) { + return null; + } + + if (bytes.length == 0) { + return ""; + } + + StringBuilder ret = new StringBuilder(bytes.length * 2); + for (byte b : bytes) { + int x = (b >> 4) & 0x0f; + int y = b & 0x0f; + ret.append(HEX_DIGITS[x]); + ret.append(HEX_DIGITS[y]); + } + return ret.toString(); + + } + + /** + * each byte is decoded from 2 hex chars + */ + public static byte[] getHexDecodedBytes(String str) { + + if (str == null) { + return null; + } + if (str.isEmpty()) { + return new byte[0]; + } + + byte[] ret = new byte[str.length() / 2]; + for (int i = 0; i < str.length(); i += 2) { + byte x = hex2byte(str.charAt(i)); + byte y = hex2byte(str.charAt(i + 1)); + ret[i / 2] = (byte) ((x << 4) + y); + } + return ret; + + } + + private static byte hex2byte(char c) { + if (c <= '9') { // 0-9 + return (byte) (c - '0'); + } + if (c <= 'F') { // A-F + return (byte) (c - 'A' + 10); + } + // a-f + return (byte) (c - 'a' + 10); + } + +} diff --git a/src/main/java/com/tractionsoftware/commons/codec/HtmlEncodingUtil.java b/src/main/java/com/tractionsoftware/commons/codec/HtmlEncodingUtil.java new file mode 100644 index 0000000..d635bd4 --- /dev/null +++ b/src/main/java/com/tractionsoftware/commons/codec/HtmlEncodingUtil.java @@ -0,0 +1,439 @@ +/* + * + * Copyright 1996-2026 Traction Software, Inc. + * + * Licensed 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. + * + */ + +// PLEASE DO NOT DELETE THIS LINE - make copyright depends on it. + +package com.tractionsoftware.commons.codec; + +import com.tractionsoftware.commons.io.StringWriteUtil; +import com.tractionsoftware.commons.lang.StringUtil; +import com.tractionsoftware.commons.text.CharBasedFilteringTextMapper; +import com.tractionsoftware.commons.text.TextWrapUtil; +import org.apache.commons.lang3.StringUtils; + +import java.io.IOException; +import java.io.PrintWriter; +import java.util.Objects; +import java.util.regex.Pattern; + +public final class HtmlEncodingUtil { + + /** + * Not instantiable. + */ + private HtmlEncodingUtil() { + } + + public enum SimpleHtmlEntity { + + QUOTATION_MARK('"', "quot"), + + GREATER_THAN('>', "gt"), + + LESS_THAN('<', "lt"), + + AMPERSAND('&', "amp"), + + NON_BREAKING_SPACE(' ', "nbsp"); + + public static SimpleHtmlEntity getForLiteral(char c) { + return switch (c) { + case '>' -> SimpleHtmlEntity.GREATER_THAN; + case '<' -> SimpleHtmlEntity.LESS_THAN; + case '&' -> SimpleHtmlEntity.AMPERSAND; + default -> null; + }; + } + + public static SimpleHtmlEntity getForTagAttributeValue(char c) { + return switch (c) { + case '"' -> QUOTATION_MARK; + case '>' -> GREATER_THAN; + case '<' -> LESS_THAN; + case '&' -> AMPERSAND; + default -> null; + }; + } + + public static SimpleHtmlEntity getForClassicConversion(char c) { + return switch (c) { + case '"' -> QUOTATION_MARK; + case '>' -> GREATER_THAN; + case '<' -> LESS_THAN; + case '&' -> AMPERSAND; + case ' ', StringUtil.CHAR_NON_BREAKING_SPACE -> NON_BREAKING_SPACE; + default -> null; + }; + } + + public static SimpleHtmlEntity get(char c) { + return switch (c) { + case '"' -> QUOTATION_MARK; + case '>' -> GREATER_THAN; + case '<' -> LESS_THAN; + case '&' -> AMPERSAND; + case StringUtil.CHAR_NON_BREAKING_SPACE -> NON_BREAKING_SPACE; + default -> null; + }; + } + + public static SimpleHtmlEntity get(String s, int index) { + + char c = s.charAt(index); + if (Character.isSurrogate(c) || c != '&') { + return null; + } + + int remaining = s.length() - index; + if (remaining < 4) { + return null; + } + + int start = index + 1; + for (SimpleHtmlEntity entity : SimpleHtmlEntity.values()) { + int len = entity.name.length(); + if (remaining > len && + entity.name.equals(s.substring(start, start + len)) && + s.charAt(start + len) == ';') { + return entity; + } + } + return null; + + } + + public static String encodeForLiteral(char c) { + SimpleHtmlEntity entity = getForLiteral(c); + if (entity == null) { + return null; + } + return entity.encoding; + } + + public static String encodeForTagAttributeValue(char c) { + SimpleHtmlEntity entity = getForTagAttributeValue(c); + if (entity == null) { + return null; + } + return entity.encoding; + } + + public static String encodeForClassicHtmlText(char c) { + SimpleHtmlEntity entity = getForClassicConversion(c); + if (entity == null) { + return null; + } + return entity.encoding; + } + + private final char value; + + private final String name; + + private final String encoding; + + SimpleHtmlEntity(char value, String name) { + this.value = value; + this.name = name; + this.encoding = "&" + name + ";"; + } + + public final void append(StringBuilder buff) { + buff.append(encoding); + } + + public final void print(PrintWriter out) { + out.print(encoding); + } + + public final void appendValue(StringBuilder buff) { + buff.append(value); + } + + public final String getEncoding() { + return encoding; + } + + public final int nameLength() { + return name.length(); + } + + public static String escapeAmpersand(char c) { + if (c == '&') { + return AMPERSAND.encoding; + } + return null; + } + + } + + private static final class HtmlLiteralAppendableWrapper implements Appendable { + + private final String preferredZeroWidthSpace; + + private final Appendable out; + + private HtmlLiteralAppendableWrapper(Appendable out, String preferredZeroWidthSpace) { + this.out = out; + this.preferredZeroWidthSpace = preferredZeroWidthSpace; + } + + @Override + public Appendable append(CharSequence csq) { + appendLiteralText(csq); + return this; + } + + @Override + public Appendable append(CharSequence csq, int start, int end) { + appendLiteralText(csq.subSequence(start, end)); + return this; + } + + @Override + public Appendable append(char c) { + String literalReplacement = encodeForLiteral(c); + if (literalReplacement != null) { + StringWriteUtil.safeAppend(out, literalReplacement); + } + else { + StringWriteUtil.safeAppend(out, c); + } + return this; + } + + private void appendLiteralText(CharSequence text) { + if (StringUtils.isNotEmpty(text)) { + CharBasedFilteringTextMapper.replace(out, text, this::encodeForLiteral); + } + } + + private String encodeForLiteral(char c) { + if (c == StringUtil.CHAR_ZERO_WIDTH_SPACE) { + return preferredZeroWidthSpace; + } + return SimpleHtmlEntity.encodeForLiteral(c); + } + + } + + + public static final String TAG_BR = "
"; + + public static final String DEFAULT_NON_SPACE_BREAK_HTML = ""; + + public static final String ZERO_WIDTH_SPACE_ENTITY_ENCODING = "​"; + + /** + * , . / \ | - % ) & > < " + */ + private static final Pattern NON_SPACE_BREAK_OPPORTUNITIES = + Pattern.compile("([,./\\\\|\\-%)]|&(amp|gt|lt|quot);)(^\\s)"); + + /** + * Applies the minimal amount of entity-encoding necessary for the given plain text to appear in literal form in an + * HTML document. This requires entity-encoding greater than, less than, and ampersand characters. + * + * @param text + * some plain text that will be appearing in an HTML document. + * @return a version of the given plain text with any tag delimiters and ampersands entity-encoded; or null if the + * given text is null. + */ + public static String getLiteralText(CharSequence text) { + if (text == null) { + return null; + } + if (text.isEmpty()) { + return ""; + } + return CharBasedFilteringTextMapper.replace(text.toString(), SimpleHtmlEntity::encodeForLiteral); + } + + /** + * Converts text to HTML in the same manner as {@link #getLiteralText(CharSequence)}, but including BR tags in place + * of line breaks. The line breaks are identified via {@link StringUtil#getLines(CharSequence)}, and the conversion + * of the individual lines' text is does with getLiteralText. + * + * @param text + * the text to be converted. + * @return some HTML representing the given text converted to HTML-safe text, plus substituting BR tags for line + * breaks. + */ + public static String getLiteralTextWithLineBreaks(CharSequence text) { + if (StringUtils.isEmpty(text)) { + return ""; + } + return StringUtil.join(StringUtil.getLines(getLiteralText(text)).iterator(), TAG_BR); + } + + public static void printLiteralTextWithLineBreaks(Appendable out, CharSequence text) { + if (StringUtils.isEmpty(text)) { + return; + } + try { + StringUtil.getNullSkippingJoiner(TAG_BR).appendTo( + out, + StringUtil.getLines(getLiteralText(text)).iterator() + ); + } + catch (IOException e) { + // This is not possible. +// Debug.logError(e, "This exception should not happen"); + } + } + + /** + * Returns a version of the given text that is safe for an HTML attribute value. This requires entity-encoding + * greater than, less than, double quotation mark, and ampersand characters. + * + * @param text + * the text to be encoded in an HTML-safe manner. + * @return a version of the given text that is safe for an HTML attribute value. + */ + public static String getTagAttributeValue(CharSequence text) { + if (text == null) { + return null; + } + if (text.isEmpty()) { + return text.toString(); + } + return Objects.toString( + CharBasedFilteringTextMapper.replace(text, SimpleHtmlEntity::encodeForTagAttributeValue), + null + ); + } + + /** + * Converts the given text to HTML, including the non-literal and usually unnecessary conversion spaces to + * non-breaking spaces. + * + *

+ * This method exists to support a handful of classic forms and a few other old use cases. It should not be used for + * new code. + * + * @param text + * the text to be converted to HTML. + * @return a conversion of the given text to HTML, including the non-literal and usually unnecessary conversion + * spaces to non-breaking spaces. + */ + public static String getClassicHtmlText(String text) { + if (StringUtils.isBlank(text)) { + return text; + } + return Objects.toString( + CharBasedFilteringTextMapper.replace(text, SimpleHtmlEntity::encodeForClassicHtmlText), + null + ); + } + + /** + * Reverses the encoding that is performed by {@link #getClassicHtmlText(String)}, translating the given HTML to + * text, including the non-literal conversion of non-breaking space entities to ordinary spaces. + * + * @param html + * the HTML to be converted to text. + * @return the decoded version of the string. + */ + public static String getClassicTextHtml(String html) { + + if (StringUtils.isEmpty(html)) { + return html; + } + + int len = html.length(); + StringBuilder buff = null; + for (int i = 0; i < len; i++) { + SimpleHtmlEntity entity = SimpleHtmlEntity.get(html, i); + if (entity == null) { + if (buff != null) { + buff.append(html.charAt(i)); + } + } + else { + if (buff == null) { + buff = new StringBuilder(len); + buff.append(html, 0, i); + } + entity.appendValue(buff); + i += entity.nameLength() + 1; + } + } + + if (buff == null) { + return html; + } + return buff.toString(); + + } + + /** + * Returns the preferred non-space optional break HTML sequence for the UserAgent being used for the current + * request, defaulting to "". + * + * @return the preferred non-space optional break HTML sequence for the UserAgent being used for the current + * request, defaulting to "". + */ + public static String getNonSpaceBreaksHtml() { + return System.getProperty( + "com.tractionsoftware.commons.codec.non_space_break_html", DEFAULT_NON_SPACE_BREAK_HTML + ); + } + + /** + * Inserts the preferred non-space optional break HTML sequence for the UserAgent being used for the current request + * where appropriate in the given text. + * + * @param text + * the text into which the non-space optional break HTML should be inserted. This must really be pure text and + * not contain any markup, since markup might be corrupted by this insertion process. + * @return the given text with the preferred non-space optional break HTML sequence for the UserAgent being used for + * the current request inserted where appropriate. + */ + public static String getHtmlWithNonSpaceBreaks(String text) { + if (text == null) { + return null; + } + return NON_SPACE_BREAK_OPPORTUNITIES.matcher(text).replaceAll("$1" + getNonSpaceBreaksHtml() + "$3"); + } + + /** + * Converts all occurrences of the ampersand character to HTML ampersand entities. + */ + public static String escapeAmps(String str) { + if (StringUtils.isEmpty(str)) { + return str; + } + return Objects.toString( + CharBasedFilteringTextMapper.replace(str, SimpleHtmlEntity::escapeAmpersand), + null + ); + } + + public static Appendable getLiteralAppendable(Appendable out, String preferredZeroWidthSpace) { + Objects.requireNonNull(out, "Appendable"); + if (StringUtils.isBlank(preferredZeroWidthSpace)) { + preferredZeroWidthSpace = TextWrapUtil.DEFAULT_ZERO_WIDTH_SPACE; + } + if (out instanceof HtmlLiteralAppendableWrapper literal && + Objects.equals(literal.preferredZeroWidthSpace, preferredZeroWidthSpace)) { + return literal; + } + return new HtmlLiteralAppendableWrapper(out, preferredZeroWidthSpace); + } + +} diff --git a/src/main/java/com/tractionsoftware/commons/codec/JavaScriptEncodingUtil.java b/src/main/java/com/tractionsoftware/commons/codec/JavaScriptEncodingUtil.java new file mode 100644 index 0000000..6885a22 --- /dev/null +++ b/src/main/java/com/tractionsoftware/commons/codec/JavaScriptEncodingUtil.java @@ -0,0 +1,247 @@ +/* + * + * Copyright 1996-2026 Traction Software, Inc. + * + * Licensed 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. + * + */ + +// PLEASE DO NOT DELETE THIS LINE - make copyright depends on it. + +package com.tractionsoftware.commons.codec; + +import com.tractionsoftware.commons.text.CharBasedFilteringTextMapper; +import org.apache.commons.lang3.StringUtils; + +/** + * Provides a handful of helpers related to JavaScript encoding. + * + * @author Andy Keller, Dave Shepperton + */ +public final class JavaScriptEncodingUtil { + + public static final String JAVASCRIPT_FILE_EXTENSION = ".js"; + + public enum CharacterRequiringEscaping { + + APOSTROPHE('\'', true), + + QUOTATION_MARK('"', true), + + BACKSLASH('\\'), + + SLASH('/'), + + NEW_LINE('n'), + + CARRIAGE_RETURN('r'), + + TAB('t'), + + FORM_FEED('f'), + + BACKSPACE('b'); + + public static CharacterRequiringEscaping get(char c) { + return switch (c) { + case '\'' -> APOSTROPHE; + case '"' -> QUOTATION_MARK; + case '\\' -> BACKSLASH; + case '/' -> SLASH; + case '\n' -> NEW_LINE; + case '\r' -> CARRIAGE_RETURN; + case '\t' -> TAB; + case '\f' -> FORM_FEED; + default -> null; + }; + } + + public static String getRequiredEscapeSequence(char c) { + CharacterRequiringEscaping value = get(c); + if (value == null) { + return null; + } + return value.escapeSequence; + } + + private final String escapeSequence; + + private final boolean quotationMark; + + CharacterRequiringEscaping(char escapingChar) { + this(escapingChar, false); + } + + CharacterRequiringEscaping(char escapingChar, boolean quotationMark) { + this.escapeSequence = "\\" + escapingChar; + this.quotationMark = quotationMark; + } + + @Override + public final String toString() { + return name() + " (" + escapeSequence + ")"; + } + + public final String getEscapeSequence() { + return escapeSequence; + } + + public final boolean isQuotationMark() { + return quotationMark; + } + + } + + private JavaScriptEncodingUtil() { + } + + /** + * Returns a version of the given text with all occurrences of certain characters escaped to ensure that the result + * is a valid JavaScript literal. Specifically, the following characters are escaped: + * + *

+ * + * @param text + * the input text. + * @return a version of the given text with a transformation applied escaping any appearances of the following + * characters to make the result a valid JavaScript literal. + */ + public static String getJavascriptLiteral(CharSequence text) { + return getJavascriptLiteral(text, false); + } + + /** + * Returns a version of the given text with all occurrences of certain characters escaped or entity encoded to + * ensure that the result is both a valid JavaScript literal and suitable for inclusion in an HTML document. + * + *

+ * Specifically, the following characters are escaped: + * + *

+ * + *

And the following characters are entity encoded: + * + *

+ * + * @param text + * the input text. + * @return a version of the given text with all occurrences of certain characters escaped or entity encoded the + * following characters to make the result both a valid JavaScript literal and suitable for inclusion in an HTML + * document. + */ + public static String getHtmlCompatibleJavascriptLiteral(CharSequence text) { + return getJavascriptLiteral(text, true); + } + + /** + * Returns a version of the given text with all occurrences of certain characters escaped to ensure that the result + * is a valid JSON literal; and, if an optional HTML-compatible version is requested, entity encoded to ensure that + * the result is also suitable for inclusion in an HTML document. + * + *

+ * Specifically, the following characters are escaped: + * + *

+ * + *

And the following characters are entity encoded if an HTML-compatible version is requested: + * + *

+ * + * @param text + * the input text. + * @param htmlCompatible + * indicating whether the + * @return a version of the given text with all occurrences of certain characters escaped or entity encoded the + * following characters to make the result both a valid JSON literal and suitable for inclusion in an HTML + * document. + */ + public static String getJavascriptLiteral(CharSequence text, boolean htmlCompatible) { + if (text == null) { + return null; + } + if (text.isEmpty()) { + return ""; + } + if (htmlCompatible) { + return CharBasedFilteringTextMapper.replace(text, JavaScriptEncodingUtil::getHtmlCompatibleLiteralReplacement); + } + return CharBasedFilteringTextMapper.replace(text, CharacterRequiringEscaping::getRequiredEscapeSequence); + } + + public static void printJavascriptLiteral(Appendable out, CharSequence text) { + printJavascriptLiteral(out, text, false); + } + + public static void printHtmlCompatibleJavascriptLiteral(Appendable out, CharSequence text) { + printJavascriptLiteral(out, text, true); + } + + public static void printJavascriptLiteral(Appendable out, CharSequence text, boolean htmlCompatible) { + if (StringUtils.isNotEmpty(text)) { + if (htmlCompatible) { + CharBasedFilteringTextMapper.replace(out, text, JavaScriptEncodingUtil::getHtmlCompatibleLiteralReplacement); + } + else { + CharBasedFilteringTextMapper.replace(out, text, CharacterRequiringEscaping::getRequiredEscapeSequence); + } + } + } + + private static String getHtmlCompatibleLiteralReplacement(char c) { + return switch (c) { + case '<' -> HtmlEncodingUtil.SimpleHtmlEntity.LESS_THAN.getEncoding(); + case '>' -> HtmlEncodingUtil.SimpleHtmlEntity.GREATER_THAN.getEncoding(); + case '&' -> HtmlEncodingUtil.SimpleHtmlEntity.AMPERSAND.getEncoding(); + default -> CharacterRequiringEscaping.getRequiredEscapeSequence(c); + }; + } + +} diff --git a/src/main/java/com/tractionsoftware/commons/codec/MD5Util.java b/src/main/java/com/tractionsoftware/commons/codec/MD5Util.java new file mode 100644 index 0000000..bf81760 --- /dev/null +++ b/src/main/java/com/tractionsoftware/commons/codec/MD5Util.java @@ -0,0 +1,290 @@ +/* + * + * Copyright 1996-2026 Traction Software, Inc. + * + * Licensed 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. + * + */ + + +// PLEASE DO NOT DELETE THIS LINE - make copyright depends on it. + +package com.tractionsoftware.commons.codec; + +import com.tractionsoftware.commons.io.FileResource; +import com.tractionsoftware.commons.io.IOUtil; +import com.tractionsoftware.commons.lang.ObjectsUtil; +import com.tractionsoftware.commons.text.SnippetUtil; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.*; +import java.math.BigInteger; +import java.security.DigestInputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +public final class MD5Util { + + private static final Logger LOGGER = LoggerFactory.getLogger(MD5Util.class); + + /* + * Not instantiable. + */ + private MD5Util() { + } + + public static final String ALGORITHM_NAME = "MD5"; + + public interface DigestResult { + + boolean wasSuccessful(); + + boolean isEmpty(); + + byte[] hashBytes(); + + String hashString(); + + String paddedHashString(); + + } + + private static final DigestResult DIGEST_RESULT_EMPTY_SUCCESS = new DigestResult() { + + @Override + public boolean wasSuccessful() { + return true; + } + + @Override + public boolean isEmpty() { + return true; + } + + @Override + public byte[] hashBytes() { + return ArrayUtils.EMPTY_BYTE_ARRAY; + } + + @Override + public String hashString() { + return ""; + } + + @Override + public String paddedHashString() { + return ""; + } + + }; + + private static final DigestResult DIGEST_RESULT_EMPTY_FAILURE = new DigestResult() { + + @Override + public boolean wasSuccessful() { + return false; + } + + @Override + public boolean isEmpty() { + return true; + } + + @Override + public byte[] hashBytes() { + return null; + } + + @Override + public String hashString() { + return null; + } + + @Override + public String paddedHashString() { + return null; + } + + }; + + private static DigestResult getResult(byte[] bytes) { + if (bytes == null) { + return DIGEST_RESULT_EMPTY_FAILURE; + } + if (bytes.length == 0) { + return DIGEST_RESULT_EMPTY_SUCCESS; + } + return new NormalDigestResult(bytes); + } + + private static final class NormalDigestResult implements DigestResult { + + private final byte[] bytes; + + private String hash; + + private String paddedHash; + + private NormalDigestResult(byte[] bytes) { + this(bytes, null, null); + } + + private NormalDigestResult(byte[] bytes, String hash, String paddedHash) { + this.bytes = bytes; + this.hash = hash; + this.paddedHash = paddedHash; + } + + @Override + public boolean wasSuccessful() { + return true; + } + + @Override + public boolean isEmpty() { + return false; + } + + @Override + public byte[] hashBytes() { + return bytes; + } + + @Override + public String hashString() { + if (hash == null) { + hash = MD5Util.getHashString(bytes); + } + return hash; + } + + @Override + public String paddedHashString() { + if (paddedHash == null) { + paddedHash = getPaddedHashString(bytes); + } + return paddedHash; + } + + } + + public static MessageDigest getMessageDigest() { + try { + return MessageDigest.getInstance(ALGORITHM_NAME); + } + catch (NoSuchAlgorithmException e) { + throw new RuntimeException("This server is missing a required component."); + } + } + + public static DigestResult digest(FileResource file) { + if (file.isDirectory()) { + return DIGEST_RESULT_EMPTY_SUCCESS; + } + return digestNonDirectoryFile(getMessageDigest(), file); + } + + public static DigestResult digest(byte[] data) { + try (ByteArrayInputStream byteInput = new ByteArrayInputStream(data)) { + return digest(getMessageDigest(), byteInput); + } + catch (IOException e) { + LOGGER.warn("Unexpected error attempting to compute an MD5 hash for bytes.", e); + return DIGEST_RESULT_EMPTY_FAILURE; + } + } + + public static DigestResult digest(InputStream input) { + try { + if (input instanceof DigestInputStream digestStream && + digestStream.getMessageDigest().getAlgorithm().equalsIgnoreCase(ALGORITHM_NAME)) { + return digestImpl(digestStream); + } + return digest(getMessageDigest(), input); + } + catch (IOException e) { + LOGGER.warn( + "Unexpected error attempting to compute an MD5 hash for stream {}", + SnippetUtil.truncatedToString(input), + e + ); + return DIGEST_RESULT_EMPTY_FAILURE; + } + } + + private static DigestResult digest(MessageDigest md5, InputStream input) throws IOException { + try (DigestInputStream digestInput = new DigestInputStream(input, md5)) { + return digestImpl(digestInput); + } + } + + public static String getHashString(byte[] digestedBytes) { + return HexUtil.getEncodedString(digestedBytes); + } + + public static String getPaddedHashString(byte[] digestedBytes) { + if (digestedBytes == null) { + return null; + } + return String.format("%1$032x", new BigInteger(1, digestedBytes)); + } + + private static DigestResult digestNonDirectoryFile(MessageDigest md5, FileResource file, String contentType) { + + if (file.isEmpty()) { + try { + return getInstanceForEmptyFile(md5, contentType); + } + catch (IOException e) { + LOGGER.warn("Failed to determine MD5 for empty file " + ObjectsUtil.safeToString(file, "file")); + return DIGEST_RESULT_EMPTY_FAILURE; + } + } + + try (InputStream input = file.getInputStream()) { + try (DigestInputStream digestInput = new DigestInputStream(input, getMessageDigest())) { + return digestImpl(digestInput); + } + } + catch (IOException e) { + LOGGER.warn("Failed to compute MD5 for file " + ObjectsUtil.safeToString(file, "file"), e); + return DIGEST_RESULT_EMPTY_FAILURE; + } + + } + + private static DigestResult digestNonDirectoryFile(MessageDigest md5, FileResource file) { + try (InputStream input = file.getInputStream()) { + return digest(md5, input); + } + catch (IOException e) { + LOGGER.warn("Failed to compute MD5 for " + ObjectsUtil.safeToString(file, "file"), e); + return DIGEST_RESULT_EMPTY_FAILURE; + } + } + + public static DigestResult getInstanceForEmptyFile(MessageDigest md5, String contentType) throws IOException { + return digest(md5, IOUtil.getStringAsUtf8InputStream(StringUtils.trimToEmpty(contentType).toLowerCase())); + } + + private static DigestResult digestImpl(DigestInputStream digestInput) throws IOException { + try (OutputStream out = OutputStream.nullOutputStream()) { + digestInput.transferTo(out); + } + return getResult(digestInput.getMessageDigest().digest()); + } + +} diff --git a/src/main/java/com/tractionsoftware/commons/config/Configuration.java b/src/main/java/com/tractionsoftware/commons/config/Configuration.java new file mode 100644 index 0000000..22e1643 --- /dev/null +++ b/src/main/java/com/tractionsoftware/commons/config/Configuration.java @@ -0,0 +1,161 @@ +/* + * + * Copyright 1996-2026 Traction Software, Inc. + * + * Licensed 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. + * + */ + +// PLEASE DO NOT DELETE THIS LINE - make copyright depends on it. + +package com.tractionsoftware.commons.config; + +import com.tractionsoftware.commons.properties.GetProperty; + +import java.util.Map; +import java.util.Objects; + +/** + * An interface representing an object type along with an associated set of configuration properties. + * + *

+ * The properties may determine the specific type itself, and may modify various behaviors of the instances to which + * they are applied. + * + * @author Dave Shepperton + */ +public interface Configuration extends GetProperty { + + /** + * Returns the logical name for this configuration. + * + * @return the logical name for this configuration. + */ + public String getName(); + + /** + * Returns the path to the file from which the Configuration properties were loaded, if applicable. + */ + public String getPath(); + + /** + * Returns the templated setting values stored in the configuration, if present. + * + * @return the templated setting values stored in the configuration, if present; null otherwise. + */ + public Map getTemplateSettings(); + + /** + * Returns a Configuration object which is identical to this one, but whose {@link #getName()} method will return + * the given name instead of this Configuration's name. + * + *

+ * If the requested name matches this Configuration's existing name, this method should generally return this + * instance. Otherwise, a new instance may be created. + * + *

+ * This default implementation uses {@link ForwardingConfiguration#withNewName(Configuration, String)}, which should + * be suitable for almost all cases. + * + * @param newName + * the requested new name. + * @return a new Configuration object which is a copy of this one, using the given name in place of this instance's + * name. + */ + public default Configuration withNewName(String newName) { + if (Objects.equals(getName(), newName)) { + return this; + } + return ForwardingConfiguration.withNewName(this, newName); + } + + /** + * This default implementation creates a {@link ConfigurationLocator}. + */ + @Override + public default Configuration withDefaults(GetProperty defaults) { + return ConfigurationLocator.getSingleConfigurationOrLocator(this, defaults); + } + + /** + * Returns a view of this Configuration that is guaranteed to supply access to the Configuration API methods only, + * or at least to methods that are deemed safe for general read-only access. For example, to pass a subclass of + * Configuration to an alien method that accepts a Configuration, and to ensure that that method cannot cast the + * argument back to the subtype in order to a to gain access to other sensitive methods, a client may invoke + * toReadOnly on that object before passing it along to that method. + * + *

+ * This default implementation returns this Configuration itself, which is a suitable implementation for subclasses + * that do not have any public methods beyond those declared in Configuration. Implementations that do have public + * methods that should not be accessible to clients that arbitrary clients when a Configuration would do should + * override it, perhaps using {@link ForwardingConfiguration#wrap(Configuration)}. Likewise, this method may be + * re-declared in subclasses using a different return type if there is a more specific safe read-only subtype that + * could be offered in place of Configuration, as this method itself overrides the declaration from {@link + * GetProperty#toReadOnly()}. + * + * @return a view of this Configuration that is guaranteed to supply access to the Configuration API methods only, + * or at least to methods that are deemed safe for general read-only access. + */ + @Override + public default Configuration toReadOnly() { + return this; + } + + /** + * This default implementation returns this Configuration instance itself, which should be suitable for all cases. + */ + @Override + public default Configuration asConfiguration() { + return this; + } + + /** + * This implementation uses {@link PropertyNameMappingConfiguration#wrapInNamespace(Configuration, String)} , which + * should be adequate for all Configuration implementations that do not need to return another specific sub-type of + * Configuration. + */ + @Override + public default Configuration getNamespace(String space) { + return PropertyNameMappingConfiguration.wrapInNamespace(this, space); + } + + /** + * This implementation uses {@link PropertyNameMappingConfiguration#wrapInNamespace(Configuration, String, char)} , + * which should be adequate for all Configuration implementations that do not need to return another specific + * sub-type of Configuration. + */ + @Override + public default Configuration getNamespace(String space, char separator) { + return PropertyNameMappingConfiguration.wrapInNamespace(this, space, separator); + } + + /** + * This implementation uses {@link PropertyNameMappingConfiguration#wrapInPrefix(Configuration, String)} , which + * should be adequate for all Configuration implementations that do not need to return another specific sub-type of + * Configuration. + */ + @Override + public default Configuration getPrefix(String prefix) { + return PropertyNameMappingConfiguration.wrapInPrefix(this, prefix); + } + + /** + * This implementation uses {@link PropertyNameMappingConfiguration#wrapInPrefix(Configuration, String, char)} , + * which should be adequate for all GetProperty implementations that do not need to return another specific sub-type + * of GetProperty. + */ + @Override + public default Configuration getPrefix(String prefix, char separator) { + return PropertyNameMappingConfiguration.wrapInPrefix(this, prefix, separator); + } +} diff --git a/src/main/java/com/tractionsoftware/commons/config/ConfigurationLocator.java b/src/main/java/com/tractionsoftware/commons/config/ConfigurationLocator.java new file mode 100644 index 0000000..7f64839 --- /dev/null +++ b/src/main/java/com/tractionsoftware/commons/config/ConfigurationLocator.java @@ -0,0 +1,66 @@ +/* + * + * Copyright 1996-2026 Traction Software, Inc. + * + * Licensed 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. + * + */ + +// PLEASE DO NOT DELETE THIS LINE - make copyright depends on it. + +package com.tractionsoftware.commons.config; + +import java.util.Map; +import java.util.Objects; + +import com.tractionsoftware.commons.properties.AbstractGetPropertyLocator; +import com.tractionsoftware.commons.properties.GetProperty; +import jakarta.annotation.Nonnull; + +/** + * Like a PropLocator, a ConfigurationLocator allows property lookups via the getProperty method to fall back from one + * store to another. This class provides an implementation of both the Configuration interface as well as an + * implementation of PropLocator that in which a Configuration is either the primary source or the fall back source, + * depending upon which constructor is used. + */ +public final class ConfigurationLocator extends AbstractGetPropertyLocator implements Configuration { + + public static final Configuration getSingleConfigurationOrLocator(Configuration locals, GetProperty defaults) { + Objects.requireNonNull(locals, "main Configuration"); + if (defaults == null) { + return locals; + } + return new ConfigurationLocator(locals, defaults); + } + + public ConfigurationLocator(Configuration locals, GetProperty defaults) { + super(locals, defaults); + } + + @Nonnull + @Override + public final String toString() { + return "Configuration: " + super.toString(); + } + + @Override + public final String getPath() { + return locals.getPath(); + } + + @Override + public final Map getTemplateSettings() { + return locals.getTemplateSettings(); + } + +} diff --git a/src/main/java/com/tractionsoftware/commons/config/DynamicForwardingConfiguration.java b/src/main/java/com/tractionsoftware/commons/config/DynamicForwardingConfiguration.java new file mode 100644 index 0000000..b99c883 --- /dev/null +++ b/src/main/java/com/tractionsoftware/commons/config/DynamicForwardingConfiguration.java @@ -0,0 +1,51 @@ +/* + * + * Copyright 1996-2026 Traction Software, Inc. + * + * Licensed 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. + * + */ + +// PLEASE DO NOT DELETE THIS LINE - make copyright depends on it. + + +package com.tractionsoftware.commons.config; + +import jakarta.annotation.Nonnull; + +import java.util.Objects; +import java.util.function.Supplier; + +public class DynamicForwardingConfiguration extends ForwardingConfiguration { + + protected final Supplier provider; + + public DynamicForwardingConfiguration(Supplier provider) { + this.provider = provider; + } + + @Nonnull + @Override + public String toString() { + return "Configuration dyn fwd {" + provider + " -> " + provider.get() + "}"; + } + + @Nonnull + @Override + protected final Configuration delegate() { + Configuration delegate = provider.get(); + Objects.requireNonNull(delegate, "dynamic delegate Configuration"); + return delegate; + } + +} diff --git a/src/main/java/com/tractionsoftware/commons/config/ForwardingConfiguration.java b/src/main/java/com/tractionsoftware/commons/config/ForwardingConfiguration.java new file mode 100644 index 0000000..11943a8 --- /dev/null +++ b/src/main/java/com/tractionsoftware/commons/config/ForwardingConfiguration.java @@ -0,0 +1,130 @@ +/* + * + * Copyright 1996-2026 Traction Software, Inc. + * + * Licensed 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. + * + */ + +// PLEASE DO NOT DELETE THIS LINE - make copyright depends on it. + +package com.tractionsoftware.commons.config; + +import java.util.Map; +import java.util.Objects; +import java.util.function.Supplier; + +import com.tractionsoftware.commons.properties.ForwardingGetProperty; +import com.tractionsoftware.commons.properties.GetProperty; +import jakarta.annotation.Nonnull; +import org.jspecify.annotations.NonNull; + +/** + * A base {@link Configuration} implementation for implementing the decorator pattern. The {@link #delegate()} method + * returns the Configuration being decorated. + * + *

+ * Methods that offer basic Configuration functionality, such as {@link #getProperty(String)}, + * {@link #getPropertyNames()} and {@link #getName()}, defer directly the corresponding method of the backing + * Configuration. + * + *

+ * Methods that return Configuration instances are guaranteed to never directly expose either the delegate or any object + * obtained directly from the delegate. They are also guaranteed not to retrieve and use the delegate instance until one + * of the basic methods is invoked. Each of these guarantees imply that such methods cannot be implemented via direct + * deference to the delegate. The resulting implementations fall into two categories: + * + *